SenseCrypt Docs
SDKs & appsMobile SDK

Error reference

Every error type and failure reason the SenseCrypt Mobile SDK can produce, what causes it, and whether it is recoverable.

The SDK has four error surfaces:

SurfaceTypeWhere it appears
Opening the SDKAuthnInitErrorThrown by open().
Loading modelsSenseCryptInitError / SenseCryptInitExceptionThrown by initializeInferenceEngine().
Starting a flowAuthnRuntimeErrorThrown by startRegistrationFlow, startRevocationFlow, startKeyRotationFlow.
Inside a flowFailureReasonCarried on failed and postingResultFailed states — not thrown.

The fourth is the one that matters most in practice: flows don't throw once started, they emit terminal states.

Initialization errors

AuthnInitError — from open()

CaseCauseRecoverable?
InvalidPortalUrl(url, details)portalUrl won't parse. Usually a trailing slash or missing scheme.Fix the config. Your bug.
SecureHardwareUnavailableNo usable secure key store on the device.No. The SDK cannot operate here. Show a terminal message.
BiometricNotEnrolledThe device has no biometric enrolled.Yes — send the user to system settings, then retry open().
Internal(details)Unexpected core failure.No. Report it.

SenseCryptInitError — from initializeInferenceEngine()

CaseCauseWhat to show
LicenseNotFound(path)No mobile.lic in the bundle or assets.Nothing — a packaging bug. Fix the project.
LicenseInvalid(reason)Present but failed parse or signature check.Terminal error.
LicenseExpiredValid but past expiry."Update your app."
ModelLoad(reason)A bundled model is missing or corrupt.Terminal; damaged artifact.
LivenessInit(reason)Liveness pipeline data missing or corrupt.Terminal; damaged artifact.

LicenseExpired is the only one of these five that is not a bug on your side. A current release ships a fresh license, so an app update is the fix — route users to the store rather than to support.

Runtime errors

AuthnRuntimeError — from flow starters

CaseCauseHandling
NoKeysstartKeyRotationFlow() with nothing to rotate.Hide the rotate action when keys is empty — but still catch this, because the last key can be revoked between your check and the tap.
UnknownKey(keyId)startRevocationFlow(keyId:) with an id this device doesn't hold.Refresh your key list; it's stale.
FlowFinished(flowId)An operation was attempted on an already-terminated flow.Start a new flow.
Internal(details)Unexpected core failure.Report it.

Failure reasons

FailureReason is carried on failed(reason:) — terminal — and on postingResultFailed(reason:), which is not terminal and can be retried with retryPostResult().

Network and server

ReasonPayloadMeaningRecoverable
networkUnreachabledetailsCouldn't reach the portal.Yes — retry.
portalErrordetailsThe portal returned an error.Sometimes; retry once.
upgradeRequiredThe server requires a newer SDK or app version.No — prompt for an app update.

Credential and session

ReasonPayloadMeaningRecoverable
tokenExpiredThe session behind the QR expired.No — restart the sign-in.
signatureRejectedThe portal rejected the device signature.No. Suggests key or clock trouble; may need re-enrollment.
clockSkewThe device clock is too far off for signed requests to validate.Yes — tell the user to enable automatic date and time.
accessDeniedThe identity is not permitted to complete this sign-in.No.

Face and liveness

ReasonPayloadMeaningRecoverable
faceMismatchMaxRetriesThe mismatch budget was exhausted. The SDK has already signed an abort to the portal.No — restart the sign-in.
livenessFailedLiveness could not be established.No at the flow level. Within a capture, liveness retries loop back without consuming budget.

User and device

ReasonPayloadMeaningRecoverable
biometricDeniedThe device biometric prompt was refused or failed.Yes — retry.
userDeclinedThe user declined the request.Terminal by intent.
loginCancelledByUserTerminal state after cancelLogin().Terminal by intent.
deviceKeyRevokedemail, clientId, appLabelThis device's key was revoked server-side.No — re-enrollment required. Name the app and email from the payload.
deviceKeyTerminatedemail, clientId, appLabelThe key was terminated server-side (a stronger revocation).No — re-enrollment required.

Registration and signup

ReasonPayloadMeaningRecoverable
pinAttemptsExhaustedToo many wrong PINs.No — restart registration.
pinExpiredThe PIN aged out.No — restart, or resend before expiry next time.
emailAlreadyExistsSignup for an address that already has an identity.No — route to sign-in instead.
signupNotAllowedThe application forbids self-signup.No — direct the user to an administrator.

Internal

ReasonPayloadMeaning
cryptoFailuredetailsA cryptographic operation failed.
internaldetailsUnexpected core failure. Also what you get from calling retryPostResult() in a state with no cached inputs.

In Swift, internal is a keyword — the case is spelled .`internal` with backticks.

Step-specific error enums

These are not failures. They are recoverable conditions carried inside non-terminal states, and the flow stays alive.

QrScanError — on awaitingQrScan

CasePayloadMeaningRe-aiming helps?
invalidQrNot a SenseCrypt sign-in QR, or the portal returned a malformed face-token blob.Yes — keep the scanner open.
expiredThe QR's expiry timestamp has passed (with a small grace window). It was a real code, it's just dead.No — surface an "QR expired" message and send the user back.
unknownKeyrecipientEmail, appLabel, appLogoBytes, appLogoMime, appPrimaryColor, appAccentColorThe QR's (recipient, client_id) pair matches no key on this device.No — offer "Add key for appLabel?"
networkThe lookup couldn't reach the portal.Yes — retry.
attestationFailedPlay Integrity (Android) / App Attest (iOS) refused to issue a token: the device failed the integrity verdict, your app is missing the required configuration, or attestation is unavailable (simulator).No — terminal. Show a device-integrity message.
accessDeniedThe server's group access gate refused this (recipient, client_id) pair — the user was removed from the app's group, or the group was deleted or detached.No — terminal. Show "contact your administrator".

invalidQr and network are worth keeping the scanner alive for; call resetToScan() to clear the error and resume. The other four cannot be fixed by re-aiming the camera — treat them as end-of-scan and route the user somewhere useful.

unknownKey carries the relying party's branding — logo bytes, MIME type, and colours — precisely so your "add a key?" prompt can render the app's identity rather than a bare client_id. Fall back to an org-initial monogram when appLogoBytes is nil.

attestationFailed fires on the simulator and emulator, where hardware attestation is unavailable. Expect it during local development; it does not mean your integration is broken.

PasswordError — on downloadingFaceToken

CaseMeaning
wrongPasswordRejected; re-prompt.
networkVerification couldn't reach the portal.

FaceCaptureRetryReason — on awaitingFaceCapture / awaitingCapture

CaseMeaningCounts against budget?
faceMismatchThe face didn't match the enrolled token.Yes.
livenessFailedLiveness wasn't established.No.

Handling patterns

Distinguish recoverable posts from hard failures. postingResultFailed means the proof is cached and one retryPostResult() away from succeeding — no re-scan, no re-capture. failed means start over. Presenting both as "something went wrong, try again" makes users redo work they don't need to.

Don't build your own retry counter for face mismatches. The budget lives in attemptsUsed / attemptsMax on the capture state, and the SDK signs the abort when it's exhausted. Render the numbers; don't reimplement the logic.

Treat deviceKeyRevoked and deviceKeyTerminated as re-enrollment triggers. Both carry the email and appLabel, so you can say exactly which enrollment is gone rather than showing a generic error.

clockSkew has a real user fix. It's the one cryptographic failure with an obvious remedy — automatic date and time. Say so explicitly instead of showing a generic signing error.

  • Initialization — where the init errors surface.
  • Login — the retry and cancel semantics in context.
  • Reference — the full type reference.

On this page