Errors
Every error type of the Folio SDK, where it reaches your code and what each case means.
Preview
This page describes a pre-release version of the SDK. Names, versions and APIs on this page can change before the release.
The SDK reports a failure in one of two ways:
- A failed call. A call on the runtime or on a store fails at once: creating the runtime,
calling a static helper, mounting a store, dispatching an action or subscribing, and on the web
also reading
statewhile no listener is subscribed. The call throws. - Store state. The work behind an action fails later, for example a request that Folio rejects.
dispatchhas already returned; the failure appears in the state of the store.
| Where it reaches you | Type |
|---|---|
mount, dispatch, subscribe, state on the web | A thrown error; see A failed call |
FolioSdk.create, SessionUiModel.error | SessionStartError |
FolioSdk.barcodeImage, computeMetrics, rotateImage | A thrown error whose message is an ImageProcessingError text |
new and parse of the calendar values, Instant.fromEpochMillis | A thrown error whose message is an InvalidCalendarValue text |
AttachmentRef.getBytes() | A thrown error whose message is an AttachmentReadError text |
AuthUiModel.flow | AuthError |
VaultUiModel.error | VaultErrorView, a wrapper |
InquiryUiModel.error | InquiryErrorView, a projection |
InquiryLifecycle.Failed | InquiryError |
MrtdUiModel.error | MrtdUiError |
FolioDocumentUiModel.error | A localized String; see Folio documents |
FolioDocumentListUiModel.reception | Failed with a localized text and retry; see Reception |
LogsUiModel.Failed, InteractionRecording.Rejected, SdkLogger calls | LogError |
Your IdentityRecordMapper | IdentityMappingError |
The embed loader's onLifecycle | InquiryError of type EMBED |
Naming across platforms
Every error type is generated from one definition, so the cases are the same on every platform. This page names cases as Kotlin does. The error types come in two shapes.
Tagged cases. InquiryError, SessionStartError, AuthError, VaultError, MrtdUiError,
ImageProcessingError, InvalidCalendarValue, AttachmentReadError, LogError and the reasons
OtpErrorReason, GovIdErrorReason, SelfieErrorReason and EmbedFailure:
| Platform | Shape | Case with fields | Case without fields |
|---|---|---|---|
| Swift | An enum with associated values, cases in lowerCamelCase with labeled fields | .recordMapping(message: message) | .invalidEntry |
| Kotlin | A sealed class: a data class per case with fields, a data object per case without | InquiryError.RecordMapping(message) | InquiryError.InvalidEntry |
| TypeScript | A value with a type in SCREAMING_SNAKE_CASE and its fields under value | { type: 'RECORD_MAPPING', value: { message } } | { type: 'INVALID_ENTRY' } |
In TypeScript each of these types also has a factory or constant per case in lowerCamelCase, such
as InquiryError.recordMapping(message) and InquiryError.invalidEntry.
ImageProcessingError, InvalidCalendarValue and AttachmentReadError are exported in these
shapes, but no call hands your app one of their values. A call that fails because of one of them throws a
failed call error whose message is the text of the case.
ImageProcessingError carries its text in a single field value: .insufficientData(value:) in
Swift, ImageProcessingError.InsufficientData(value) in Kotlin, and
{ type: 'INSUFFICIENT_DATA', value: '<text>' } in TypeScript, where value is the string itself,
not an object.
Codes. CoreError is a plain enumeration: an enum with a UInt8 raw value in Swift
(.invalidComponent), an enum class with a value in Kotlin (CoreError.INVALID_COMPONENT),
and a string in TypeScript ('INVALID_COMPONENT', also CoreError.INVALID_COMPONENT).
In TypeScript, match a case by its type:
if (lifecycle.type === 'FAILED' && lifecycle.value.error.type === 'RECORD_MAPPING') {
reportMapperFailure(lifecycle.value.error.value.message);
}The inquiry UI needs no such check: it shows the notice in InquiryUiModel.failure, whose texts the
SDK has already chosen. See Failure notice.
A failed call
A call on a store fails because of a CoreError, FolioSdk.create because of a
SessionStartError, an image helper because of an
ImageProcessingError, a calendar value constructor because of an
InvalidCalendarValue and getBytes() of an attachment because of an
AttachmentReadError. In every case the call throws the platform's error
type, whose message describes the failure; the SDK error itself is not handed to your app:
| Platform | What the call throws |
|---|---|
| iOS | FolioSDKError, with code and message. Call the method with try. After close(), every call on a store throws FolioSDKError with the message Handle already released. |
| Android | FolioSDKException, a RuntimeException with code and message. After close(), every call on a store throws IllegalStateException with the message Handle has been closed. |
| Web | FolioSDKError, an Error with message, the numeric status and lastError, and their names statusName and lastErrorName. Reading state calls into the SDK, and so can throw, only while no listener is subscribed; otherwise it returns the last state. When the store's state stream failed, reading state throws that error. After close(), dispatch and subscribe() without a listener throw an Error such as MrtdStore has been disposed, reading state returns the last state, and subscribe with a listener function returns an unsubscribe that does nothing. |
do {
try store.dispatch(action: .close)
} catch let error as FolioSDKError {
log(error.message)
} catch {
log(String(describing: error))
}On Android, rememberFolioSdk does not throw: it reports a failed create as
FolioSdkStartup.Failed with the exception in error. See Runtime and stores.
On the web, message is the SDK's message for the failure, or the status name when the SDK
recorded none. The SDK also prints every FolioSDKError it throws with console.error, prefixed
[FolioSDK]; see Create the runtime.
A store keeps its state current by reading a subscription in the
background. When that stream fails, the error is kept in streamError: a @Published Error? on
iOS and a StateFlow<Throwable?> on Android. On the web the store keeps it and throws it from
state.
Error codes
The code of FolioSDKError on iOS and of FolioSDKException on Android, and status and
lastError of FolioSDKError on the web, are codes of the bridge between your app and the SDK's
native code. The numbers are the same on every platform:
| Code | Name | Kotlin constant | TypeScript |
|---|---|---|---|
| 0 | Ok | FolioSDKException.OK | FolioSDKErrorCode.Ok |
| 1 | InvalidArgument | FolioSDKException.INVALID_ARGUMENT | FolioSDKErrorCode.InvalidArgument |
| 2 | NullPointer | FolioSDKException.NULL_POINTER | FolioSDKErrorCode.NullPointer |
| 3 | BufferTooSmall | FolioSDKException.BUFFER_TOO_SMALL | FolioSDKErrorCode.BufferTooSmall |
| 4 | InvalidUtf8 | FolioSDKException.INVALID_UTF8 | FolioSDKErrorCode.InvalidUtf8 |
| 5 | TypeMismatch | FolioSDKException.TYPE_MISMATCH | FolioSDKErrorCode.TypeMismatch |
| 6 | InvalidHandle | FolioSDKException.INVALID_HANDLE | FolioSDKErrorCode.InvalidHandle |
| 7 | NotSupported | FolioSDKException.NOT_SUPPORTED | FolioSDKErrorCode.NotSupported |
| 8 | InternalError | FolioSDKException.INTERNAL_ERROR | FolioSDKErrorCode.InternalError |
| 9 | Panic | FolioSDKException.PANIC | FolioSDKErrorCode.Panic |
| 10 | Timeout | FolioSDKException.TIMEOUT | FolioSDKErrorCode.Timeout |
| 11 | Cancelled | FolioSDKException.CANCELLED | FolioSDKErrorCode.Cancelled |
| 12 | NotFound | FolioSDKException.NOT_FOUND | FolioSDKErrorCode.NotFound |
| 13 | AlreadyExists | FolioSDKException.ALREADY_EXISTS | FolioSDKErrorCode.AlreadyExists |
| 14 | PermissionDenied | FolioSDKException.PERMISSION_DENIED | FolioSDKErrorCode.PermissionDenied |
| 15 | IoError | FolioSDKException.IO_ERROR | FolioSDKErrorCode.IoError |
| 16 | RefcountOverflow | FolioSDKException.REFCOUNT_OVERFLOW | FolioSDKErrorCode.RefcountOverflow |
| 17 | CallbackDestroyed | FolioSDKException.CALLBACK_DESTROYED | FolioSDKErrorCode.CallbackDestroyed |
| 18 | WrongThread | FolioSDKException.WRONG_THREAD | FolioSDKErrorCode.WrongThread |
| 19 | ReentrancyViolation | FolioSDKException.REENTRANCY_VIOLATION | FolioSDKErrorCode.ReentrancyViolation |
| 20 | JniException | FolioSDKException.JNI_EXCEPTION | FolioSDKErrorCode.JniException |
| 21 | Forged | FolioSDKException.FORGED | FolioSDKErrorCode.Forged |
| 22 | Misaligned | FolioSDKException.MISALIGNED | FolioSDKErrorCode.Misaligned |
| 23 | BorrowConflict | FolioSDKException.BORROW_CONFLICT | FolioSDKErrorCode.BorrowConflict |
| 255 | Unknown | FolioSDKException.UNKNOWN | FolioSDKErrorCode.Unknown |
In Swift, code is a FolioSDKErrorCode; compare its number, error.code.rawValue, a UInt32.
In Kotlin, code is an Int and the constants are members of FolioSDKException. In TypeScript,
FolioSDKErrorCode is exported from @folio/sdk, and statusName and lastErrorName hold the
names from this table, or unnamed(<code>) for a number the table does not list.
BorrowConflict means that the call needed an SDK object in a way that conflicts with its current
use: exclusive access to an object that is in use elsewhere, or any access to an object that
another call holds exclusively. Its message is handle has conflicting shared or exclusive access.
A call that fails because of an SDK error, such as a CoreError, a SessionStartError, an
ImageProcessingError, an InvalidCalendarValue or an AttachmentReadError, throws with code
InternalError (8); the SDK error shows only in message, as its text. The numeric codes of
CoreError are not bridge codes.
Bridge failures
Some interfaces of the SDK are implemented by your app, such as the
IdentityRecordMapper you pass to FolioSdk.create. The SDK reports a
failure of such an implementation to a bridge error handler when a method that cannot return an
error throws, or when the arguments or the return value of a method cannot be converted. It reports
the failure of a store's stream to the same handler. The handler receives the site,
<Interface>.<method> or <Store>.stream, and the error.
| Platform | Handler | Default |
|---|---|---|
| iOS | FolioSDKCore.bridgeErrorHandler: (@Sendable (String, Error) -> Void)? | NSLog |
| Android | FolioSDKCore.bridgeErrorHandler: ((String, Throwable) -> Unit)? | System.err |
| Web | FolioSDKDiagnostics.bridgeErrorHandler?: (site: string, error: Error) => void | console.error |
Set the handler to send these failures to your own logging, for example to the
SDK logger. An error that a method can return, such as an IdentityMappingError
from map, reaches the SDK as that error and is not reported, and neither is cancellation. A store
stream failure also stays visible in the store's streamError, as described in
A failed call.
CoreError
The error of the store runtime. Each case has a fixed numeric code: the raw value in Swift and
value in Kotlin.
| Case | Code | Swift | Kotlin | TypeScript | Meaning |
|---|---|---|---|---|---|
InvalidComponent | 0 | .invalidComponent | CoreError.INVALID_COMPONENT | 'INVALID_COMPONENT' | The call targets a component that does not match or no longer exists. |
DeterminismViolation | 1 | .determinismViolation | CoreError.DETERMINISM_VIOLATION | 'DETERMINISM_VIOLATION' | The runtime detected a breach of its internal consistency rules. |
NotInitialized | 3 | .notInitialized | CoreError.NOT_INITIALIZED | 'NOT_INITIALIZED' | The runtime is not initialized. |
StoreOverflow | 4 | .storeOverflow | CoreError.STORE_OVERFLOW | 'STORE_OVERFLOW' | The action could not be queued: the runtime's queue is full or the store loop stopped. |
TimelineUnavailable | 5 | .timelineUnavailable | CoreError.TIMELINE_UNAVAILABLE | 'TIMELINE_UNAVAILABLE' | The debug history cannot be read or written, for example after the runtime has closed it. A release build records no debug history; in a build with the timeline feature (the debug profile), the runtime keeps it in memory, up to 2,048 entries, and drops it when it shuts down. |
TimelineCorrupted | 6 | .timelineCorrupted | CoreError.TIMELINE_CORRUPTED | 'TIMELINE_CORRUPTED' | A record of the debug history fails its integrity check or cannot be decoded. |
StorageUnavailable | 7 | .storageUnavailable | CoreError.STORAGE_UNAVAILABLE | 'STORAGE_UNAVAILABLE' | Stored state cannot be read or written. |
ClockBeforeEpoch | 8 | .clockBeforeEpoch | CoreError.CLOCK_BEFORE_EPOCH | 'CLOCK_BEFORE_EPOCH' | The device clock reads a time before the Unix epoch. |
ValueOutOfRange | 10 | .valueOutOfRange | CoreError.VALUE_OUT_OF_RANGE | 'VALUE_OUT_OF_RANGE' | A count or a timestamp does not fit its exported type. The SDK never clamps it. |
ArenaFull | 11 | .arenaFull | CoreError.ARENA_FULL | 'ARENA_FULL' | No more stores can be mounted on this runtime. |
Unmounted | 12 | .unmounted | CoreError.UNMOUNTED | 'UNMOUNTED' | The store is no longer mounted because its runtime was shut down. |
InvalidInput | 13 | .invalidInput | CoreError.INVALID_INPUT | 'INVALID_INPUT' | A value you passed is not valid or cannot be decoded, for example an organization that is not valid. |
SourceUnavailable | 14 | .sourceUnavailable | CoreError.SOURCE_UNAVAILABLE | 'SOURCE_UNAVAILABLE' | The store's last state was computed from inputs that are no longer active and no newer state is ready yet. Reported by a state read (mount, and on the web reading state while no listener is subscribed) and by dispatch while the store's inputs are revoked and no renewed state has been accepted yet; the store stays mounted, and dispatch succeeds again once it has taken the renewed state. |
LogsUnavailable | 15 | .logsUnavailable | CoreError.LOGS_UNAVAILABLE | 'LOGS_UNAVAILABLE' | The SDK's logging could not start or finish: the logging configuration is not valid, or the log journal could not be written when the runtime shut down. Reported by FolioSdk.create, shutdown() and close(). |
The actions of the session, authentication, vault, Folio document and logs stores wait in one queue
of at most 256 actions, shared by all the stores mounted on the runtime. When the queue is full,
dispatch throws StoreOverflow. Every action of the inquiry store except the PreviewFrame
intent, and every action of the NFC store, skips this queue, so it goes through even when the queue
is full. A PreviewFrame that arrives while the queue is full is dropped, and its dispatch does
not throw.
SessionStartError
FolioSdk.create fails with it, and SessionUiModel.error carries it when the last sign-out
failed. See Session.
| Case | Fields | Meaning |
|---|---|---|
Backend | status, code, message | Folio rejected the session start or refresh. status is the HTTP status; code and message come from Folio. |
Network | message | Folio could not be reached. |
Scope | message | The services of the new identity could not be started, or the runtime was closed while the session changed. |
Core | error (CoreError) | A local failure, such as invalid configuration or unavailable storage. |
status is UInt16 in Swift, Int in Kotlin and number in TypeScript.
ImageProcessingError
The error of the image helpers FolioSdk.barcodeImage, FolioSdk.computeMetrics and
FolioSdk.rotateImage. The helper throws a failed call error with code
InternalError (8) whose message is the text of the case. Every case except EmptyImageData
carries a text that describes the failure.
| Case | Meaning |
|---|---|
InsufficientData | rotateImage: the ARGB data is not exactly width × height × 4 bytes. computeMetrics: the data is shorter than the width, height, format and stride require. |
InvalidDimensions | The width or height is zero or otherwise not valid. |
EmptyImageData | The image holds no pixel data. |
InvalidStride | The stride, the bytes per row, is too small for the width and format. |
ProcessingError | Any other failure, for example an empty barcode value, an unknown barcode type or a value the type cannot encode. |
The message is the text of the case alone; no case name or prefix precedes it. For example,
barcodeImage with an empty value throws empty barcode value, barcodeImage of an Ean13
barcode with the value abc throws
barcode encode failed: IllegalArgumentException - Requested contents should be 12 or 13 digits long, but got 3,
and computeMetrics of a 10 × 10 RGBA image with no data throws
data length (0) < expected (400) for 10x10 Rgba.
InvalidCalendarValue
The failures of the calendar values. When a part is out of
range or the text is not in the accepted format, new and parse throw a
failed call error with code InternalError (8) whose message is the text of the
case, such as "2026-02-29" is not a calendar date for Date. No call returns the case as a
value.
| Case | Fields | Raised by |
|---|---|---|
Date | text | CalendarDate.new, CalendarDate.parse |
Month | text | CalendarMonth.new, CalendarMonth.parse |
Time | text | ClockTime.new, ClockTime.parse |
DateTime | text | No call of your app; a date and time text that cannot be decoded |
EpochMillis | millis | Instant.fromEpochMillis for an instant outside the years 0000 to 9999 |
text is the value that was rejected. For new it is the parts written as ISO 8601 text, such as
2026-02-29. millis is Int64 in Swift, Long in Kotlin and bigint in TypeScript.
AttachmentReadError
The failures of getBytes() on an AttachmentRef, such as a photo of a
Folio document. The call throws a failed call
error with code InternalError (8) whose message is the text of the case, such as
attachment access has expired for Expired. No call returns the case as a value.
| Case | Fields | Meaning |
|---|---|---|
Expired | The reference no longer grants access: the session that listed it has ended, or access to its record was revoked. Use the reference the store publishes next. | |
NotFound | The record or the attachment no longer exists. | |
ReadFailed | message | The bytes could not be read from the vault. |
InvalidMetadata | message | The checksum recorded for the file is not valid. |
SizeMismatch | expected, actual | The bytes read do not have the recorded size. |
ChecksumMismatch | The bytes read do not match the recorded checksum. |
expected and actual are sizes in bytes: UInt64 in Swift, Long in Kotlin and bigint in
TypeScript.
AuthError
The error of a sign-in. It reaches you in AuthUiModel.flow:
- A retryable error, while a step waits for input, comes as
AwaitingInputwith anerror. The person can try again on the same step. - A terminal error, or a retryable one while no step waits, comes as
Failedwith anerror. The sign-in ended; start it again.
See Authentication.
| Case | Fields | Meaning | Retryable |
|---|---|---|---|
WrongPassword | The password is wrong, or it does not unlock the account key Folio sent for the step. | yes | |
WrongCode | The one-time or authenticator code is wrong or expired. | yes | |
Expired | The sign-in session, challenge, intent or token expired. | no | |
WrongRecoveryCode | The recovery code is wrong. | yes | |
PasskeyFailed | The passkey operation failed. | yes | |
PasskeyCancelled | The person cancelled the passkey prompt. | yes | |
WeakPassword | The new password was not accepted. | yes | |
Locked | until, optional | Too many attempts; Folio reported when the sign-in unlocks, as until. | yes |
RateLimited | retryAfter, optional | Too many attempts; wait before the next one. | yes |
Network | Folio could not be reached. | yes, while a step waits | |
Unsupported | capability | The device does not support what the step needs, for example "passkey". | yes |
RecoveryRequired | The account must be recovered before it can sign in. | no | |
NotPermitted | Folio does not allow the operation for this account, device, identifier or credential. | no | |
ServerError | Folio failed, or rejected the request as invalid. | no | |
TokenPersistFailed | The session tokens could not be saved on the device, or removed from it. | no | |
KeyUnavailable | The device's key storage failed or holds unusable key material. | no | |
SessionUnavailable | message | The sign-in succeeded but the SDK could not switch the session to the new identity. | no |
Unknown | Any other failure. While a password, code, recovery code or new password step waits, it is reported as that step's wrong-input error instead. | yes, while a step waits |
until of Locked is a Unix time in seconds: Int64? in Swift, Long? in Kotlin and
bigint | undefined in TypeScript. retryAfter of RateLimited is a wait in seconds: UInt64? in
Swift, Long? in Kotlin and bigint | undefined in TypeScript.
VaultErrorView
VaultUiModel.error wraps the vault's error with its code:
| Field | Meaning |
|---|---|
errorCode | The code of the case: vault. and the case name in lowerCamelCase, such as vault.notFound. |
details | The VaultError. Match on its case to react to the error. |
VaultError
The error of the vault. It reaches you in details of VaultUiModel.error. See
Vault and Sharing.
| Case | Fields | Meaning |
|---|---|---|
NotFound | message | The record was not found. |
PermissionDenied | The account has no permission for the operation. | |
Revoked | Access to the record was revoked. | |
PolicyDenied | A policy does not allow the operation. | |
RevisionConflict | message | The record changed since it was read. |
IdempotencyConflict | message | A repeated request conflicts with an earlier one. |
InvalidRequest | message | Folio rejected the request as invalid. |
ChallengeFailed | message | The answer to a sharing challenge is wrong. |
InviteExpired | message | The invite expired. |
InviteNotPending | message | The invite is no longer pending. |
InviteNotFound | message | The invite was not found. |
QuotaExceeded | message | The storage quota is used up. |
RecordTooLarge | message | The record is too large. |
TokenInvalid | message | A token is not valid. |
AttachmentNotFound | message | The attachment was not found. |
Unauthorized | message | The request is not authorized. |
EmptyUpdate | message | The update changes nothing. |
Codec | message | Data could not be encoded or decoded. |
Crypto | message | An encryption or decryption step failed. |
Api | code, message | Any other error from Folio, with its code. |
Storage | message | Local storage failed. |
Custody | message | The device's key custody failed. |
DraftNotFound | handle | The draft was not found. |
InvalidWrappedKey | message | A wrapped key is not valid. |
MissingIdentityKey | The identity key is missing. | |
UnsupportedRecordType | recordType | The record type is not supported. |
Offline | The device is offline. | |
OfflineFolderOpNotSupported | Folder operations are not supported while offline. | |
UpstreamUnavailable | message | A service behind Folio is unavailable. |
AttachmentNotUploaded | The attachment was not uploaded. | |
AttachmentClientIdMismatch | The attachment does not belong to the record. | |
AttachmentCorrupted | recordClientId, attachmentClientId | The attachment is corrupted. |
TransientNotSupported | The operation is not supported for a guest session. | |
EncoderUnavailable | format | The platform has no encoder for this image format. |
RecordNotApplied | recordId, message | A change to the record was not applied. |
RecordIdNotReserved | recordId | The record id is not reserved for this account. |
RecordIdMismatch | expected, received | Folio returned another record id than the one sent. |
UnsupportedBodyVersion | version | The record body version is not supported. |
UnknownSchema | schema | The record schema is not registered. |
SchemaMismatch | recordId, expected, received | The record carries another schema than expected. |
InvalidRegistry | message | The vault registry is not valid. |
UnregisteredSchema | schema | No handler is registered for the schema. |
FileTooLarge | limitBytes | A file is larger than limitBytes bytes. |
format of EncoderUnavailable is an ImageFormat. version of UnsupportedBodyVersion and
limitBytes of FileTooLarge are UInt32 in Swift, Long in Kotlin and number in TypeScript.
InquiryErrorView
InquiryUiModel.error is not the raw error but its projection for the screen:
| Field | Meaning |
|---|---|
text | Optional. The text to show. When it is empty, show the step's generic error text. |
placement | Field with the field the error belongs to, or Banner. |
retryable | Whether to offer a retry, which sends the Retry intent. |
See Errors in the model for the placement rules.
InquiryError
The error of an inquiry. It reaches you as the raw value in InquiryLifecycle.Failed, and it is
the source of InquiryUiModel.error. The placement and retryable columns describe that projection,
an InquiryErrorView: where the error is shown, with which text, and whether
it offers a retry. A failure of a Resume entry before its first step never offers one. The field
of an Otp error is value, the contactField or codeField of the step. A case that says the
update is not applied leaves the previous step on screen.
| Case | TypeScript type | Code | Fields | Meaning | Placement | Retryable |
|---|---|---|---|---|---|---|
InvalidEntry | INVALID_ENTRY | inquiry.invalidEntry | The entry has a blank launch code or handoff code, or an inquiry id that is given but blank. | Banner, no text | no | |
BootFailed | BOOT_FAILED | inquiry.bootFailed | message | The inquiry runtime failed to start, for example because its files did not load. | Banner, no text | no |
Embed | EMBED | inquiry.embed | reason, message | The embed loader failed. See Embed errors. | Banner, no text | no |
LaunchInvalid | LAUNCH_INVALID | inquiry.launchInvalid | Folio could not tell who launched the inquiry. | Banner, no text | no | |
LaunchCodeInvalid | LAUNCH_CODE_INVALID | inquiry.launchCodeInvalid | Folio does not accept the launch code: it was already used, has expired or is not valid. A Start that fails with it before its first step shows the launch code notice. | Banner, no text | no | |
TokenInvalid | TOKEN_INVALID | inquiry.tokenInvalid | Folio reported the inquiry's token as not valid. | Banner, no text | no | |
TokenExpired | TOKEN_EXPIRED | inquiry.tokenExpired | Folio reported the inquiry's token as expired. | Banner, no text | no | |
NotStarted | NOT_STARTED | inquiry.notStarted | The inquiry has not started. | Banner, no text | no | |
NotController | NOT_CONTROLLER | inquiry.notController | Folio rejected a request because another device controls the inquiry. The store makes this device an observer and reads the inquiry again, so the model shows the observer screen instead. Two requests are the exception: when Folio rejects the request of ContinueHere that takes control, or the read of the result once the inquiry reaches its end, the error reaches the model as a banner. See Control of the inquiry. | Banner, no text | no | |
InvalidState | INVALID_STATE | inquiry.invalidState | The inquiry is in a state that does not allow the request. A selfie capture that is incomplete or does not match its state fails the update with it, and the update is not applied. A resumed identity save whose mapper returns a different body, and a government id verification while no session holds a vault, fail the verification with it; the inquiry stays open. See The SDK checks each capture and Identity records. | Banner, no text | no | |
AlreadyCompleted | ALREADY_COMPLETED | inquiry.alreadyCompleted | The inquiry already completed, or already started. | Banner, no text | no | |
CompletionIncomplete | COMPLETION_INCOMPLETE | inquiry.completionIncomplete | The inquiry cannot complete yet. | Banner, no text | no | |
StepNotAvailable | STEP_NOT_AVAILABLE | inquiry.stepNotAvailable | There is no current step to act on. | Banner, no text | no | |
InvalidStepType | INVALID_STEP_TYPE | inquiry.invalidStepType | The intent does not fit the current step. | Banner, no text | no | |
RequirementUnresolvable | REQUIREMENT_UNRESOLVABLE | inquiry.requirementUnresolvable | A requirement of the inquiry cannot be met. | Banner, no text | no | |
MaxAttempts | MAX_ATTEMPTS | inquiry.maxAttempts | The maximum number of attempts is reached. | Banner, no text | no | |
AttemptInProgress | ATTEMPT_IN_PROGRESS | inquiry.attemptInProgress | Another attempt is still running. | Banner, no text | no | |
HandoffExpired | HANDOFF_EXPIRED | inquiry.handoffExpired | The handoff code expired. | Banner, no text | no | |
HandoffConsumed | HANDOFF_CONSUMED | inquiry.handoffConsumed | The handoff code was already used. | Banner, no text | no | |
HandoffMismatch | HANDOFF_MISMATCH | inquiry.handoffMismatch | The handoff belongs to another user or another region. | Banner, no text | no | |
SchemaValidation | SCHEMA_VALIDATION | inquiry.schemaValidation | message | The submitted data does not match the step. The banner shows message. | Banner with message | no |
FieldValidation | FIELD_VALIDATION | inquiry.fieldValidation | message | A submitted field is not valid. The banner shows message. | Banner with message | no |
NotFound | NOT_FOUND | inquiry.notFound | The inquiry was not found. | Banner, no text | no | |
Expired | EXPIRED | inquiry.expired | The inquiry expired. | Banner, no text | no | |
Api | API | inquiry.api | code, message | Any other error from Folio. code is Folio's error code. | Banner, no text | only for error.inquiry.internal_error |
Otp | OTP | inquiry.otp | reason, message | An email or phone code step failed. See OTP reasons. | Field at value with message for a reason marked at the field; otherwise Banner, no text | no |
GovId | GOV_ID | inquiry.govId | reason, code, message | A government ID step failed. code is the error code of Folio's verifier, such as error.gov_id_nfc.cached.malformed_jwt, and is absent (nil in Swift, null in Kotlin, undefined in TypeScript) when the SDK raised the error itself. See GovId reasons. | Banner, no text | no |
Selfie | SELFIE | inquiry.selfie | reason, message | A selfie step failed. See Selfie reasons. | Banner, no text | no |
NoActiveInquiry | NO_ACTIVE_INQUIRY | inquiry.noActiveInquiry | There is no inquiry to act on, for example a same-device resume on a device with no saved inquiry. | Banner, no text | no | |
WindowAttestationInvalid | WINDOW_ATTESTATION_INVALID | inquiry.windowAttestationInvalid | message | The signed attestation Folio returns for a launch code could not be verified: it is expired, names another inquiry, is malformed or does not match its claims, its signature does not verify, or Folio's key set has no compatible key for it. | Banner, no text | no |
Upload | UPLOAD | inquiry.upload | message | A file upload failed. | Banner, no text | no |
Network | NETWORK | inquiry.network | message | Folio could not be reached. | Banner, no text | yes |
Codec | CODEC | inquiry.codec | message | Data could not be encoded or decoded. | Banner, no text | no |
Crypto | CRYPTO | inquiry.crypto | message | An encryption step failed. | Banner, no text | no |
Storage | STORAGE | inquiry.storage | message | Local storage failed. | Banner, no text | no |
Platform | PLATFORM | inquiry.platform | message | A platform service failed, a camera preview frame has the wrong size, or an Edit does not fit its field: a value type the field does not take, or a phone country outside its options. | Banner, no text | no |
RecordMapping | RECORD_MAPPING | inquiry.recordMapping | message | Your IdentityRecordMapper threw, or the SDK could not read an identity image as a picture. Nothing was saved, and the inquiry ends as Failed. | Banner, no text | no |
FileTooLarge | FILE_TOO_LARGE | inquiry.fileTooLarge | limitBytes | An image of the verification result that the SDK attaches to the identity record is larger than limitBytes, 32 MiB. The SDK does not call your IdentityRecordMapper, nothing was saved, and the inquiry ends as Failed. | Banner, localized text with the limit | no |
FileUnreadable | FILE_UNREADABLE | inquiry.fileUnreadable | field | A picked file could not be read; your UI sent FileUnreadable. | Field at field, localized text | no |
InvalidLink | INVALID_LINK | inquiry.invalidLink | url | A link of the step, or the terminal redirect, has a url that does not parse, or the liveness service address of a selfie state does not parse or is not the Folio selfie proxy of the SDK (scheme, origin, path, no user name, query or fragment). The update is not applied. | Banner, no text | no |
LinkRefused | LINK_REFUSED | inquiry.linkRefused | url | The device refused to open a valid link; your UI sent LinkRefused. | Banner, localized text | no |
MissingCaptureField | MISSING_CAPTURE_FIELD | inquiry.missingCaptureField | step | A selfie photo step names no field to upload the photo to. step is the step id. The update is not applied. | Banner, no text | no |
MissingFieldLabel | MISSING_FIELD_LABEL | inquiry.missingFieldLabel | field | A field of the step that needs a label has none. The update is not applied. | Banner, no text | no |
InvalidCodeLength | INVALID_CODE_LENGTH | inquiry.invalidCodeLength | step, length | A one-time code step asks for a code length outside 4 to 8. The update is not applied. | Banner, no text | no |
UnknownCountry | UNKNOWN_COUNTRY | inquiry.unknownCountry | field, code | A phone field, or the phone of a phone code step, names a country it cannot offer: one without a dial code, or a default country outside its countries. The update is not applied. | Banner, no text | no |
MissingDefaultCountry | MISSING_DEFAULT_COUNTRY | inquiry.missingDefaultCountry | field | A phone field has no default country. The update is not applied. | Banner, no text | no |
InvalidDate | INVALID_DATE | inquiry.invalidDate | field, value | The default, earliest or latest date of a date field is not an ISO date. The update is not applied. | Banner, no text | no |
UnsupportedDateFormat | UNSUPPORTED_DATE_FORMAT | inquiry.unsupportedDateFormat | field, format | A date field has a date format the SDK cannot mask. The update is not applied. | Banner, no text | no |
UnsupportedTimeZone | UNSUPPORTED_TIME_ZONE | inquiry.unsupportedTimeZone | field, zone | A date field names a time zone the device does not know. The update is not applied. | Banner, no text | no |
InvalidWeekday | INVALID_WEEKDAY | inquiry.invalidWeekday | field, day | A date field disables a weekday outside 0 (Sunday) to 6 (Saturday). The update is not applied. | Banner, no text | no |
MissingDateFieldOffset | MISSING_DATE_FIELD_OFFSET | inquiry.missingDateFieldOffset | field | A date field that checks against today has no resolved UTC offset. The edit is not applied. | Banner, no text | no |
LocaleNotSaved | LOCALE_NOT_SAVED | inquiry.localeNotSaved | message | The language choice could not be saved; nothing was sent to Folio. | Banner, no text | yes |
length of InvalidCodeLength and day of InvalidWeekday are Int64 in Swift, Long in
Kotlin and bigint in TypeScript. limitBytes of FileTooLarge is UInt32 in Swift, Long in
Kotlin and number in TypeScript.
code of GovId is optional: String? in Swift and Kotlin, string | undefined in TypeScript. In
Swift the case is .govId(reason:code:message:).
The code names the error in the SDK's debug log: a failed inquiry request is logged as the warning
inquiry effect failed with the code in error_code, for example inquiry.tokenExpired. See
Logging.
OTP reasons
OtpErrorReason, the reason of an Otp error. The reasons marked "at the field" are shown at the
step's value field with the message from Folio: the contact field (contactField) while the email
address or phone number is entered, and the code field (codeField) while the code is entered.
InvalidTarget, MissingTarget and DeliveryFailed concern the contact. The others are banners
without text.
When RecoveryRequired comes back while the SDK runs a verification command of the step, the SDK
reports the command to Folio as rejected instead of showing the error, and Folio decides how the
step goes on. The same holds for the selfie reason RecoveryRequired.
| Reason | Meaning | Shown |
|---|---|---|
RecoveryRequired | Folio holds no verification session for the step any more, for example a code confirmed before one was sent. | banner |
InvalidCode | The code is wrong. | at the field |
ExpiredCode | The code expired. | at the field |
AttemptsExhausted | No attempts are left. | at the field |
ConfirmExhausted | No confirmation attempts are left. | at the field |
InvalidTarget | The email address or phone number is not valid. | at the field |
MissingTarget | The email address or phone number is missing. | at the field |
DeliveryFailed | The code could not be delivered. | at the field |
StepInactive | The step is no longer active. | banner |
NotFound | The verification was not found. | banner |
IdempotencyConflict | A repeated request conflicts with an earlier one. | banner |
Forbidden | The request is not allowed. | banner |
ConfigInvalid | The step's configuration is not valid. | banner |
System | The verification service failed. | banner |
Other | Any other reason. | banner |
GovId reasons
GovIdErrorReason, the reason of a GovId error. It covers the document photo check, the NFC
chip check and the face match. A Cached reason concerns a verification result saved earlier that
the inquiry presented again instead of a new capture.
| Reason | Meaning |
|---|---|
DocCountryDisallowed | Documents of this country are not accepted. |
DocExpired | The document expired. |
DocElectronicReplica | The capture shows an electronic replica, not the physical document. |
DocTamperingDetected | The document shows signs of tampering. |
DocMrzChecksumFailed | A check digit of the machine readable zone is wrong. |
DocRecognizeFailed | The document could not be recognized. |
NfcUnsupported | The chip data needed for the step is not available. |
NfcPaFailed | Passive authentication of the chip data failed. |
NfcChainFailed | The certificate chain of the chip could not be verified. |
NfcRevoked | A certificate of the chip was revoked. |
NfcNonConformant | The chip data does not conform to the standard. |
NfcDataIntegrityFailed | The integrity check of the chip data failed. |
NfcDataValidityFailed | The validity check of the chip data failed. |
NfcDg1Missing | The chip data has no DG1. |
FaceNoMatch | The faces do not match. |
FaceEngineUnavailable | The face matching service is not available. |
FaceEngineUnavailableTransient | The face matching service is not available right now. |
FaceMatchFailed | The face match could not be performed. |
ChecksFailed | The document checks failed. |
CachedPathEngineBlocked | A saved result cannot be used for this step. |
CachedMalformedJwt | The saved result is malformed. |
CachedInvalidSignature | The signature of the saved result is not valid. |
CachedWrongIssuer | The saved result comes from another issuer. |
CachedSubjectMismatch | The saved result belongs to another subject. |
CachedExpired | The saved result expired. |
CachedArtifactMismatch | The files of the saved result do not match it. |
CachedSchemaIncompatible | The saved result has an incompatible format. |
CachedMissingCheck | The saved result lacks a required check. |
CachedPolicyOutdated | The saved result was made under an outdated policy. |
CachedStale | The saved result is too old. |
CachedNonPassResult | The saved result is not a pass. |
CachedVerifierDecommissioned | The verifier of the saved result is no longer in service. |
ConflictingContexts | The request conflicts with the context of the step. |
AttemptsExhausted | No attempts are left. |
StepInactive | The step is no longer active. |
NotFound | The verification was not found. |
AlreadyCompleted | The verification already completed. |
RateLimited | Too many requests. |
ProcessingUnavailable | The document processing service is not available. |
ProcessingInProgress | The document processing is still running. |
ProcessingTransactionNotFound | The document processing transaction was not found. |
ConfigInvalid | The step's configuration is not valid. |
IdempotencyConflict | A repeated request conflicts with an earlier one. |
Forbidden | The request is not allowed. |
ReportFailed | The verification report could not be produced. |
System | The verification service failed. |
Other | Any other reason. |
Selfie reasons
SelfieErrorReason, the reason of a Selfie error.
| Reason | Meaning |
|---|---|
CapturedImageMissing | The selfie photo was not uploaded before the step was completed. |
CapturedImageInvalid | The uploaded selfie photo is not a valid image: its encoding, size, content type or file signature failed the check. |
CapturedImageNotAllowed | The step takes no uploaded photo, because it runs a liveness check. |
ProxyDisabledInMock | The liveness service was called for a step that runs in mock mode. |
ProxySessionMismatch | The traffic to the liveness service does not match the step's capture session. |
AgeOutOfRange | The estimated age is outside the allowed range. |
FaceCoveringDetected | The face is covered. |
GlassesViolation | The glasses rule of the step is violated. |
ImageQualityFailed | The image quality check failed. |
PortraitQualityFailed | The portrait quality check failed. |
PortraitMissing | The liveness check returned no portrait. |
DetectFailed | The face detection service failed. |
RecoveryRequired | Folio holds no capture session for the step any more. |
LivenessFailed | The liveness check failed. |
FaceNoMatch | The selfie does not match the document photo. |
FaceMatchFailed | The face match could not be performed. |
FaceEngineUnavailable | The face matching service is not available. |
FaceEngineUnavailableTransient | The face matching service is not available right now. |
ChecksFailed | The selfie checks failed. |
AttemptsExhausted | No attempts are left. |
StepInactive | The step is no longer active. |
NotFound | The verification was not found. |
AlreadyCompleted | The verification already completed. |
LivenessUnavailable | The liveness service is not available. |
LivenessInProgress | The liveness check is still running. |
LivenessSessionExpired | The liveness session expired. |
LivenessTransactionNotFound | The liveness transaction was not found. |
ConfigInvalid | The step's configuration is not valid. |
IdempotencyConflict | A repeated request conflicts with an earlier one. |
Forbidden | The request is not allowed. |
ReportFailed | The verification report could not be produced. |
System | The verification service failed. |
Other | Any other reason. |
Embed errors
The embed loader reports its own failures through onLifecycle as a FAILED lifecycle whose
error is an InquiryError. See Inquiry embed.
error.type | reason.type | Meaning |
|---|---|---|
EMBED | INVALID_OPTIONS | The mount options are not valid: an empty app.version or app.build, an entry that is not an InquiryEntry, a missing or blank backdrop.color, panel.background or panel.shadow, a container that is not found, no baseUrl to use, or an allowedOrigin that is '*' or cannot be derived. message names the problem. |
EMBED | READY_TIMEOUT | The iframe did not report READY within 15 seconds after it was added. |
EMBED | ORIGIN_REJECTED | A message came from another origin or window than the one the loader trusts. |
EMBED | VERSION_MISMATCH | A loader pinned to a major version got a runtime of another major version, or one that reports no version. Load the loader of the matching major version. |
BOOT_FAILED | The runtime or its WASM failed to load. |
EmbedFailure, the reason of Embed, has the cases InvalidOptions, ReadyTimeout,
OriginRejected and VersionMismatch. In TypeScript the fields sit under value, for example
lifecycle.value.error.value.reason.type.
MrtdUiError
The error of an NFC chip read, in MrtdUiModel.error. Every case has a message, the text to show
in the runtime's current locale, which the SDK chooses for the cause. See
NFC chip reading for the text of each cause.
| Case | Swift | TypeScript type | Meaning |
|---|---|---|---|
InvalidMrz | .invalidMrz(message:) | INVALID_MRZ | The document number, date of birth or date of expiry is not valid. |
ConnectionLost | .connectionLost(message:) | CONNECTION_LOST | The connection to the chip broke, or the NFC session was cancelled, not found or not available. |
Authentication | .authentication(message:) | AUTHENTICATION | The chip refused access or an authentication step with the chip failed. |
ChipNotSupported | .chipNotSupported(message:) | CHIP_NOT_SUPPORTED | The device has no NFC reader or the SDK has no NFC access on the platform, which is always the case in a browser; or the chip does not support the read. |
ReadFailed | .readFailed(message:) | READ_FAILED | NFC is switched off on the device; or a file of the chip could not be read or parsed, or the platform refused the NFC session. An optional data group that the chip reports as absent is skipped instead. |
Internal | .internal(message:) | INTERNAL | Any other failure. |
IdentityMappingError
The failure of your IdentityRecordMapper. IdentityMappingError is a value with one field,
message: a struct in Swift, a data class in Kotlin and an interface in TypeScript.
To report a failure, throw from map: any Error on iOS and the web, any exception on Android.
The SDK turns what you throw into an IdentityMappingError with its message. On the web the
message is the error's message. On iOS it is message of a thrown FolioSDKError; otherwise it
is errorDescription of a LocalizedError when that is set, and String(describing:) of the error
otherwise. The mapper runs before any write, so the SDK then saves nothing, and the inquiry ends
with InquiryLifecycle.Failed and the InquiryError RecordMapping with that message. See
Identity records.
Selfie capture
How the inquiry UI captures a selfie with liveness, the selfie capture provider on iOS and Android and the web liveness component.
Localization
The SDK string catalog, the runtime locale, the inquiry language pick and the static helpers for text, countries, phone numbers, addresses and dates.