Skip to content

Error handling

The SDK never throws plain Error objects. Every failure is an IntelliAuthError with a stable code field you can branch on. This is the single error type your app needs to handle.

class IntelliAuthError extends Error {
code: IntelliAuthErrorCode // stable enum, listed below
message: string // human-readable, may change between versions
details?: Record<string, unknown> // extra context, code-specific
cause?: unknown // wrapped underlying error if any
recoverable: boolean // hint about whether a retry makes sense
}

Match on code. Do not match on message — message text is not part of the API and may improve over time without notice.

The codes group into four families.

CodeWhenRecoverable?
session_expiredThe user's session has expired since the last token refresh.Yes — call loginWithRedirect().
session_revokedAn admin revoked the session, or the user revoked it from another device.Yes — sign in again.
not_authenticatedA method that requires a session was called without one.Yes — sign in.
silent_auth_failedBackground session check could not complete.Sometimes — usually network.
CodeWhen
state_mismatchThe state returned to /callback does not match what the SDK sent. CSRF defence kicked in.
invalid_redirect_uriThe redirect URI does not match what's registered on the application.
consent_requiredThe user has not yet granted the requested scopes.
mfa_requiredThe platform demands a second factor. Use completeMfaStep.
step_up_requiredThe current AAL is too low for the action. Use stepUp.
CodeWhenRecoverable?
network_errorThe fetch itself failed (DNS, TCP, TLS).Yes — retry with backoff.
rate_limitedThe user or app hit a rate limit.Yes — wait, look for Retry-After.
server_errorThe platform returned a 5xx.Yes — retry; consider a circuit breaker.
not_foundThe requested entity doesn't exist or you can't see it.No — usually a config issue.
CodeWhen
webauthn_failedThe WebAuthn ceremony did not complete.
totp_invalidThe submitted TOTP code was wrong or expired.
sms_send_failedThe SMS delivery did not succeed.
backup_code_invalidThe submitted backup code was wrong or already consumed.

The error code index lists every code in the SDK with the recommended user-facing copy.

const { loginWithRedirect } = useIntelliAuth()
async function trySignIn() {
try {
await loginWithRedirect()
} catch (e) {
if (!(e instanceof IntelliAuthError)) throw e
switch (e.code) {
case 'mfa_required':
// The SDK already started the MFA flow. Show the MFA step UI.
showMfaPrompt(e.details?.flow_id)
break
case 'state_mismatch':
// Don't retry silently — there may be a real CSRF attempt.
showError('Sign-in security check failed. Please try again from the home page.')
break
case 'rate_limited':
showError(`Too many attempts. Try again in ${e.details?.retry_after_s ?? 60}s.`)
break
default:
showError(e.message)
}
}
}

The provider supports onError. Use it for telemetry — not for user-facing UI:

<IntelliAuthProvider
/* ... */
onError={(err) => {
telemetry.captureException(err, { sdk: 'intelliauth' })
}}
>

Per-call try/catch is still required for user-facing branching; onError is the firehose.

If you want to throw application-level errors that also happen to wrap an IntelliAuthError — say, a "PaymentBlockedError" that fires when MFA is required for a payment — use the cause:

try {
await api.charge({ amount: 100 })
} catch (e) {
if (e instanceof IntelliAuthError && e.code === 'step_up_required') {
throw new PaymentBlockedError('Confirm your identity to continue.', { cause: e })
}
throw e
}

The downstream error handler can still match cause against IntelliAuthError when it needs to drive a step-up.

IntelliAuthError serialises sensibly via JSON.stringifycode, message, details come out cleanly. The cause chain is not serialised by default; if you log via a structured logger, include cause explicitly.

Never log access tokens or refresh tokens. The SDK does not put them in error details; your code shouldn't either.