Events & Error handling
Events
The SDK emits events so you can show spinners and handle analytics:
client.on('start', (payload) => {
console.log('Started:', payload.operation) // 'signUp' | 'signIn' | 'enroll'
showSpinner()
})
client.on('success', (payload) => {
console.log('Success:', payload.operation)
hideSpinner()
})
client.on('error', (payload) => {
console.error('Error:', payload.error)
hideSpinner()
showError(payload.error.message)
})
// Unsubscribe
const unsubscribe = client.on('start', handler)
unsubscribe()
Events: 'start', 'success', 'error', 'cancelled'.
Error handling
All methods return Result<T, TryMellonError>. Check result.ok and use result.error.code:
import { isTryMellonError } from '@trymellon/js'
const result = await client.signIn({ externalUserId: 'user_123' })
if (!result.ok) {
const error = result.error
switch (error.code) {
case 'USER_CANCELLED':
console.log('User cancelled')
break
case 'NOT_SUPPORTED':
await client.otp.send({
userId: 'user_123',
email: '[email protected]',
})
break
case 'PASSKEY_NOT_FOUND':
await client.signUp({ externalUserId: 'user_123' })
break
case 'NETWORK_FAILURE':
// Often caused by unallowed Origin in TryMellon dashboard (CORS rejection)
console.error('Network error:', error.details)
break
case 'TIMEOUT':
console.error('Operation timed out')
break
default:
console.error(error.code, error.message)
}
return
}
// result.value has sessionToken, user, etc.
Error codes
| Code | Description |
|---|---|
NOT_SUPPORTED | WebAuthn not available in this environment |
USER_CANCELLED | User cancelled the operation |
PASSKEY_NOT_FOUND | No passkey found for this user |
SESSION_EXPIRED | Session or QR session expired |
NETWORK_FAILURE | Network error (SDK retries with backoff) |
INVALID_ARGUMENT | Invalid argument in config, options, or origin not allowed |
TIMEOUT | Operation timed out |
ABORT_ERROR | Operation aborted via AbortSignal or browser AbortError |
CHALLENGE_MISMATCH | Link already used, replayed, or context hash mismatch |
RATE_LIMIT_EXCEEDED | Too many requests — SDK backs off and retries automatically |
TICKET_NOT_FOUND | Enrollment ticket not found or invalid |
TICKET_EXPIRED | Enrollment ticket has expired |
TICKET_ALREADY_USED | Enrollment ticket was already consumed |
PIN_MISMATCH | Bridge presence PIN does not match |
PIN_LOCKED | Bridge PIN locked after too many failed attempts |
BRIDGE_SESSION_EXPIRED | Bridge session expired or not found |
ACTION_CHALLENGE_EXPIRED | Action signing challenge TTL exceeded (default 5 min) — issue a new challenge |
ACTION_ALREADY_CLAIMED | Action challenge already verified — anti-replay; each challenge is single-use |
ACTION_PAYLOAD_MISMATCH | Payload hash signed by the device doesn’t match what the server issued |
FORBIDDEN | Access denied — missing required role or scope |
SERVER_ERROR | Unrecoverable server-side error |
UNKNOWN_ERROR | Unexpected error — check error.details for context |
Troubleshooting CORS & Network Errors
If your users experience silent failures or you see NETWORK_FAILURE in the SDK with a (null) status in your browser’s Network tab for OPTIONS preflight requests, this typically means CORS rejection.
Ensure that the domain from which the SDK is running (e.g., https://your-app.com) has been added to your Application’s Allowed Origins list in the TryMellon dashboard. TryMellon enforces strict dynamic origin validation to protect your users.
Cancel with AbortSignal
const controller = new AbortController()
setTimeout(() => controller.abort(), 10000)
const result = await client.signUp({
externalUserId: 'user_123',
signal: controller.signal,
})
if (!result.ok && result.error.code === 'ABORT_ERROR') {
console.log('Cancelled')
}