Go to developer

Errors & Troubleshooting

Use error codes when you have them; use symptoms when you do not.

On this page

Error shape

Partner API errors return JSON: { "error": string, "message": string, "details"?: object }. TypeScript SDK maps many cases to typed errors; others surface as KeyraServerError with .status, .code, and .body.

API error codes

ErrorHTTPMeaningCommon causeFix
invalid_client401Bad credentialsWrong clientId/secret or publishable key used as secretUse secret key; format Bearer clientId:clientSecret
not_enrolled404No active identityChallenge before enrollment / after revokeRun enable2FA first
already_enrolled409Identity already activeCalling enroll twiceUse status check; go to challenge flow
already_consumed409Token reusedDouble consume / retry without new challengeCreate new challenge for a new login
invalid_token401Wrong verification tokenStale/mismatched tokenUse token from the approved poll for that challengeId
expired401/410Token or link expiredWaited too long after approval / enrollmentRestart enrollment or challenge
not_found404Unknown idBad enrollment/identity/challenge idVerify ids; check project scope
invalid_return_url400returnUrl rejectedMissing callback config / origin mismatchAlign returnUrl with project callback URL
invalid_request400Validation failedBad projectId UUID / externalUserIdFix request body (externalUserId ≤ 128)
forbidden403Project mismatchConsume across projectsUse credentials for the challenge's project
rate_limited429Too many requestsAggressive polling / abuseHonor pollAfterMs; back off

SDK exceptions (TypeScript)

ClassWhenRetry?
KeyraAuthenticationErrorHTTP 401 invalid_clientNo — fix credentials
KeyraTimeoutErrorwait helpers exceeded timeoutOptional — start a new challenge/enrollment
KeyraChallengeDeniedErrorstatus deniedYes — new challenge after user retry
KeyraChallengeExpiredErrorstatus expiredYes — new challenge
KeyraEnrollmentExpiredErrorenrollment terminal EXPIREDYes — new enable2FA
KeyraEnrollmentCancelledErrorCANCELLEDYes — new enable2FA
KeyraEnrollmentFailedErrorFAILED (e.g. revoked/suspended identity)Investigate identity state
KeyraIdentityErrordisable/recovery/codes with no identityEnroll first
KeyraNetworkErrorTransport/timeoutTransient — retry with backoff
KeyraServerErrorOther non-2xxInspect .code / status

Troubleshooting by symptom

Enrollment QR opens but nothing completes

  • Possible cause: User abandoned flow; enrollment expired (10 minutes); polling wrong enrollmentId.
  • Diagnose: pollEnrollment status / terminal.
  • Fix: Start a new enable2FA; ensure UI polls the same id.

Challenge remains PENDING

  • Possible cause: User has not opened QR; wrong URL shown; challenge already expired.
  • Diagnose: Confirm challengeUrl encoding; check expiresIn; poll status.
  • Fix: Re-display QR quickly; start a new challenge if expired.

User approved but login doesn't complete

  • Possible cause: Backend never called consume; lost verificationToken by re-polling; started a new challenge on “complete”.
  • Diagnose: Log challengeId continuity; ensure consume runs once with token from wait helper.
  • Fix: Reuse challengeId → wait → consume → then create your session.

Challenge expired

  • Possible cause: User took longer than ~2 minutes.
  • Fix: Start a new challenge; improve UX messaging.

Identity shows as not enrolled

  • Possible cause: Never enrolled; disabled/revoked; different externalUserId / project.
  • Fix: Verify projectId + externalUserId stability; re-enroll.

401 from Partner API

  • Possible cause: Incorrect Bearer format; sandbox/prod mix; using only sk_* without clientId: prefix.
  • Fix: Authorization: Bearer cp_…:sk_… with matching environment.

Works locally but fails in production

  • Possible cause: Still using _test_ keys; returnUrl/callback mismatch; wrong base URL.
  • Fix: Switch to cp_prod_/sk_prod_ and production projectId; verify callback configuration.