Errors and reason codes
Camera errors, check reasons, SDK-side reasons and API error codes.
Camera / engine errors (onError, cap.on('error'))
kind | Cause | What to show |
|---|---|---|
permission | User denied the camera, or Permissions-Policy blocks it | How to allow the camera; a retry button |
notfound | No camera on the device | Suggest a phone |
inuse | Another app holds the camera | Close it, retry |
insecure | Page is not HTTPS / secure context | Fix the deployment |
unsupported | No getUserMedia | Suggest another browser |
generic | Engine failure (models could not load — often CSP or missing assets — recorder failure, clip too large, constraints) | Retry; check §7–§8 in development |
Check reasons (result.reason, steps[].reason, webhook reason)
| Check | Reason | Retryable | Billed | Meaning |
|---|---|---|---|---|
population_registry | nni_not_found | no | yes | NNI absent from the registry |
nni_invalid | no | yes | Malformed / unusable NNI | |
registry_unavailable | yes | no | Registry outage, no answer (the SDK retries once after 5 s) | |
result_from_cache | — | yes | Pass served from the registry cache (informational) | |
nni_recheck | yes | no | Face match failed against the portrait of a typed NNI: the number is asked again once (likely typo) | |
nationality_not_allowed · underage · age_unverifiable | no | yes | Profile eligibility rule on the registry record | |
name_mismatch · birth_date_mismatch · nationality_mismatch | no | yes | Your subject data disagrees with the registry record | |
liveness | no_face | yes | yes | No face found in the clip |
media_unreadable | yes | no | Clip could not be decoded | |
clip_too_short | yes | no | Clip shorter than required | |
liveness_inconclusive | yes | yes | Mid-band score in automatic decision mode → capture again | |
device_integrity_failed | yes | no | Device attestation failed or missing (enforce, native apps) | |
spoof_detected | no | yes | Presentation attack signal | |
review_band | no | yes | Mid-band score → human review | |
uncalibrated_capture_channel | no | yes | Non-mobile capture on a legacy review path → human review | |
face_match | no_face_source | yes | yes | No face in the keyframe (re-capture can fix it) |
no_face_reference | no | yes | No face in the registry portrait (reference-side; a re-take cannot fix it) | |
low_quality_reference | no | yes | Reference portrait too poor | |
below_threshold | no | yes | Definitive non-match | |
match_inconclusive | yes | yes | Mid-band match in automatic decision mode → capture again | |
probe_not_from_liveness | yes | yes | The face image isn't the person in the liveness clip. Only face matching is retried (keep your face in the frame, then try again); liveness is not reopened, and the new keyframe is checked against the clip that already passed | |
review_band | no | yes | Mid-band score → human review | |
subject_revoked | no | no | Returning-user session only: the subject was revoked or deleted after the session was created. Never reaches the model. Create a full verification session | |
doc_ocr | side_missing · unreadable | yes | no | A side is missing or could not be read → photograph it again |
nationality_unreadable | yes | no | The MRZ nationality could not be read (never replaced by the issuing state) → clearer photo | |
document_authenticity | yes | yes | Authenticity warning in automatic decision mode → capture again | |
document_type_not_accepted | yes | yes | The profile does not accept this document type → present another | |
age_unverifiable | yes | yes | minAge set but the birth date could not be read → clearer photo | |
mrz_checksum_failed · expired_document | no | yes | MRZ check digits fail / document expired | |
authenticity_review · registry_mismatch | no | yes | Authenticity concern / civil fields differ from the registry → human review | |
document_nni_mismatch | no | yes | The document's NNI differs from the NNI bound at session create | |
nationality_not_allowed · underage | no | yes | Profile eligibility rule | |
name_mismatch · birth_date_mismatch · nationality_mismatch · document_number_mismatch | no | yes | Your subject data disagrees with the document | |
phone_otp | otp_mismatch · otp_expired · otp_send_failed | yes | no | Wrong code / code expired / send failed → re-enter or resend |
otp_channel_unavailable | yes | no | The chosen channel is not offered for this number → pick another | |
otp_destination_not_allowed · otp_destination_unknown | yes | no | The number's country is not allowed / not identifiable → enter another number | |
otp_rate_limited | yes | no | Resend cooldown → wait, then retry | |
email_otp | otp_mismatch · otp_expired · otp_send_failed · otp_rate_limited | yes | no | As for phone_otp |
email_invalid · email_disposable | yes | no | Malformed or refused address / disposable domain blocked → enter another | |
portrait_icao | icao_pose · icao_eyes · icao_mouth · icao_occlusion · icao_lighting · icao_background · icao_resolution | yes | yes | The first failing ICAO criterion → capture the portrait again |
portrait_not_from_liveness | yes | yes | The portrait isn't the person in the liveness clip | |
media_unreadable | yes | no | No usable image / face | |
eid_auth | eid_identifier_invalid | yes | no | The national number was refused → enter it again |
eid_code_invalid · eid_code_expired | yes | no | Wrong or expired code → type it again, or ask for a new code | |
eid_resend_too_soon | yes | no | Wait until resendAvailableAt | |
eid_resend_limit | yes | no | No more codes (3 resends) → type the last code received, or use the camera path | |
eid_unavailable | yes | no | The provider is down → try again, or use the camera path | |
eid_not_enrolled | no | no | The number has no account in the provider's app → use the camera path when offered | |
eid_locked | no | no | Too many wrong codes → use the camera path when offered | |
nationality_not_allowed · underage · age_unverifiable · name_mismatch · birth_date_mismatch · nationality_mismatch | no | no | The eID verified the person, then a profile rule or your subject data rejected the session | |
nfc_chip | chip_read_failed · chip_access_denied · chip_parse_failed | yes | no | Native SDKs only: read again |
chip_not_present · nfc_unavailable · chip_document_mismatch · chip_integrity_failed · registry_mismatch · document_nni_mismatch | no | no | Native SDKs only: final | |
platform_unsupported | no | no | Skip reason of a preferred chip step on the web |
Only a passed (reason-less) OTP check is billed; a chip read bills when it passes or goes to review.
Retryable reasons park the check at input_required while attempts remain
(4 per check by default; a flow can override) and surface as
status: 'retry' with autoRetry on. Non-retryable reasons settle the check.
Step input errors (InvalidInputError, OtpInputError, §4.5)
field | kind | Cause |
|---|---|---|
phone | format | Not an E.164 number (+222…) |
code | format | Not a 6-digit code |
nni | format | Not 10 digits |
nni | check_digit | 10 digits, wrong check digit (isValidNni() checks it ahead of time) |
identifier | format / check_digit | eid step: the national number (the NNI in Mauritania), same checks as nni |
email | format | Not an email address (OtpInputError) |
channel | required / unavailable | Several channels offered and none chosen, or a channel the step does not offer (OtpInputError) |
Nothing was submitted or billed; ask for the same field again.
Result screens (result step display, §4.5)
display | When | What to show |
|---|---|---|
approved | Completed, approved | Success (your server confirms) |
review | Parked for human review, or completed with outcome review | "We are reviewing your verification" |
rejected | Failed without a policy reason | "Your identity could not be verified" |
expired | The session expired | Offer a new session |
policy_stop | A profile rule stopped the session (failureReason: location_not_allowed, nationality_not_allowed, underage, document_type_not_accepted, …) | The rule, without blaming the user |
cooldown | device_cooldown; retryAfter says when | "Try again after …" |
attempts_exhausted | A check used all its attempts | Offer a new session later |
platform_unsupported | The profile needs a step the web cannot run (mandatory chip reading); result.status is unavailable | "This verification needs a phone app." |
SDK-side reasons (status: 'unavailable')
no_recorder · attestation · network · registry_unavailable · nni_unbound · (none) on
session expiry — see §5. Session failureReason platform_unsupported (a
mandatory chip step on the web) also reports unavailable (§4.5).
API error codes you may meet
| Code | HTTP | Where |
|---|---|---|
nni_locked | 409 | A browser submitted an NNI different from the bound one |
contact_locked | 409 | The phone/email choice can no longer change (a contact step passed), or the session has no choice |
identity_confirm_required | 409 | A live-photo submit before the person confirmed the identity summary |
identity_confirm_locked | 409 | No identity summary is pending, or a retake is no longer possible |
consent_required | 4xx | A submit before consent (the SDK records consent first; you only see this when composing calls yourself) |
attestation_invalid | 4xx | Turnstile token rejected |
session_not_active | 4xx | Session expired or terminal |
rate_limit_exceeded | 429 | §7 |
idempotency_key_collision, idempotency_in_progress | 409 | §3 |
@sekyro/web exports HostedApiError { status, code, retryAfterSeconds? } and
KNOWN_CODES / isKnownCode(); @sekyro/node throws
SekyroApiError { status, code, requestId, retryable }. Quote requestId
when contacting support.