{/* TECHNICAL REVIEW NOTES (not published by ReadMe)
WEB SDK errorCode VALUES: The onError callback and Workflow manager error state both surface an optional numeric errorCode. The values this code can take are not documented in any source files reviewed. Confirm the full enumeration with the Web SDK team before publishing, or remove the errorCode row from the Web SDK error surface table.
iOS IncdFlowError ENUMERATION: Only two cases are documented in source files (.interrupted and .apiKeyRevoked). Confirm the full IncdFlowError enum with the iOS SDK team — there are likely additional cases not surfaced in the documentation files reviewed.
iOS SelfieScanError VALUES: Referenced in face login result documentation but never enumerated. Confirm values with iOS SDK team and add to the Face Login section.
iOS IncdError VALUES: Referenced as the associated value of NFCScanError.error() but never enumerated. Confirm values with iOS SDK team and add to the NFC section.
ANDROID ResultCode DESCRIPTIONS: The Javadoc enum lists SUCCESS, ERROR, USER_CANCELLED, and EMULATOR_DETECTED with no descriptions. Confirm whether descriptions exist or should be authored, and whether any additional ResultCode values exist beyond these four.
FLUTTER / REACT NATIVE / XAMARIN: These platforms are noted as wrapping the native SDKs but were not reviewed. Verify that the error models are derivative before publishing the note in the "Other Platforms" section, and replace with platform-specific detail if the error surface differs meaningfully.
PLACEHOLDER LINKS: Several cross-reference links use placeholder slugs (marked [LINK]). Replace with correct doc slugs before publishing.
END REVIEW NOTES */}
Incode SDKs surface errors differently from the Omni API. Rather than HTTP status codes, SDK errors appear as typed values delivered through callbacks, delegate methods, or state machine transitions — depending on the platform and integration path.
This page covers error handling for the Web SDK 2.0, iOS SDK, and Android SDK. For API error codes, see API Error Codes.
Error handling model by platform
Before diving into platform specifics, it helps to understand the structural difference between how each SDK surfaces errors:
| Platform | Fatal flow errors | Module-level errors | Configuration errors |
|---|---|---|---|
| Web SDK 2.0 | error state on manager; onError callback on <incode-flow> |
captureStatus === 'uploadError' on capture managers |
Thrown at runtime for unrecognized workflow steps |
| iOS | onError(_ error: IncdFlowError) delegate method |
Per-module result error enums (e.g. NFCScanError) |
Runtime. No dedicated configuration exception |
| Android | onError(error: Throwable) listener method |
resultCode on module result objects |
ModuleConfigurationException thrown at build() |
Web SDK 2.0
The Web SDK 2.0 uses a state machine model. Every manager exposes an error terminal state, and the <incode-flow> component surfaces fatal errors via a callback.
Fatal errors
Headless managers (Phone, Email, Selfie, ID Capture, Workflow, Orchestrated Flow) all share the same error state shape:
// Subscribe to state changes on any manager
manager.subscribe((state) => {
if (state.status === 'error') {
console.error('Fatal error:', state.error);
// state.error is a string describing the failure
}
});
<incode-flow> component surfaces fatal errors via its onError callback:
flow.onError = (error: string | undefined, errorCode?: number) => {
console.error('Flow error:', error, errorCode);
};
{/* REVIEW: Confirm errorCode numeric values with Web SDK team. See review note 1. */}
| Property | Type | Description |
|---|---|---|
error |
string | undefined |
Human-readable description of the error |
errorCode |
number (optional) |
Numeric error code providing additional detail. See review note 1 for documentation gap. |
The Workflow manager error state also includes an optional errorCode:
// WorkflowState when status === 'error'
{ status: 'error', error: string, errorCode?: number }
Capture errors
During active capture, errors surface as sub-state properties rather than as a separate error state. These are inline failures the user can retry, not fatal flow terminations.
Selfie and ID Capture managers — when captureStatus === 'uploadError':
| Property | Type | Description |
|---|---|---|
uploadError |
string? |
Error code identifying the upload failure |
uploadErrorMessage |
string? |
Human-readable error message |
uploadErrorDescription |
string? |
Detailed error description |
Retry is available when canRetry === true. Call manager.retryCapture() to retry.
Camera permission errors
When status === 'permissions' and permissionStatus === 'denied', the user has denied camera access. This is not a fatal error. Prompt the user to enable camera permissions in their browser settings, then call manager.requestPermission() again.
Unrecognized workflow steps (headless mode only)
In headless mode using createOrchestratedFlowManager, if the workflow returns a step whose module key is not registered, the manager throws:
"No registered module found for: <KEY>"
This does not apply to the <incode-flow> component, which renders a fallback "Module not available" screen and advances the flow automatically.
iOS SDK
The iOS SDK uses a delegate pattern. Fatal flow errors are delivered to a single onError method on IncdOnboardingDelegate, while module-level errors are returned as typed enum values within each module's result struct.
Fatal flow errors
All fatal errors during an onboarding flow are delivered via:
func onError(_ error: IncdFlowError) {
// Handle fatal flow error
}
The documented IncdFlowError cases are:
| Case | Description |
|---|---|
.interrupted |
The flow was interrupted programmatically via forceInterrupt() or dismiss(forceInterrupt: true). |
.apiKeyRevoked(apiKey) |
The API key used to initialize the SDK was revoked mid-session. The associated value contains the revoked key. Use this case to trigger API key rotation. |
{/* REVIEW: Confirm the full IncdFlowError enumeration with iOS SDK team. See review note 2. */}
Info
API key rotation
When .apiKeyRevoked is received, reinitialize the SDK with a new API key using IncdOnboardingManager.shared.initIncdOnboarding(url:apiKey:), then restart or resume the session. See the API Key Rotation guide for full details.
Handling programmatic interruption
To force-stop the current flow:
// Attempt to mark session as finished, then trigger onError(.interrupted)
IncdOnboardingManager.shared.forceInterrupt(tryFinishingFlow: true) { success, error in
// Cleanup complete
}
// Or dismiss without finishing:
IncdOnboardingManager.shared.dismiss(forceInterrupt: true)
Module result errors
Each module callback returns a result struct that includes an error property. Errors here indicate a module-level failure, not necessarily a fatal flow error.
AES (Advanced Electronic Signature)
func onAesCompleted(_ result: AESResult) {
if let error = result.error {
// Handle AES error
}
}
AESError values:
| Value | Description |
|---|---|
.noDocuments |
No documents are available for the current onboarding session. |
.failedToSign |
The AES signing operation failed. |
NFC Scan
func onNFCScanCompleted(_ result: NFCScanResult) {
if let error = result.error {
// Handle NFC error
}
}
NFCScanError values:
| Value | Description |
|---|---|
.error(IncdError) |
An underlying SDK error occurred. The associated IncdError value provides additional detail. |
.notAvailable |
NFC scanning is not available on this device. |
.userDocumentHasNoChip |
The user indicated their document does not have an NFC chip. |
.noScanAttemptsRemaining |
The user exhausted all NFC scan attempts without success. |
{/* REVIEW: Confirm IncdError enum values with iOS SDK team. See review note 4. */}
Face Login / Selfie Scan
Face login results include a SelfieScanError in the error property of SelfieScanResult. Spoof detection is surfaced separately via the spoofAttempt boolean:
IncdOnboardingManager.shared.startFaceLogin() { result in
if let error = result.error {
// A SelfieScanError occurred
}
if result.spoofAttempt == true {
// Liveness check failed — spoof detected
}
}
{/* REVIEW: Confirm SelfieScanError enum values with iOS SDK team. See review note 3. */}
On-Demand Resources error
If you are using On-Demand Resources (ODR) and call an onboarding method before the resources have been downloaded, the method returns a .resourcesNotFound error. Always call downloadOnDemandResources() and wait for onCompleted before starting any onboarding modules.
Simulator behavior
On iOS Simulator, modules that require the camera (ID Scan, Selfie Scan, Video Selfie, and others) show a black screen for 2 seconds, then return .simulatorDetected and advance to the next module. Ensure testMode: true is set during initialization when running on Simulator.
Android SDK
The Android SDK uses a listener pattern. Fatal errors are delivered as Throwable objects to onError() on OnboardingListener. Module results carry a ResultCode indicating the outcome.
Fatal flow errors
override fun onError(error: Throwable) {
// Fatal flow error — log error.message for details
IncodeWelcome.getInstance().deleteUserLocalData()
}
@Override
public void onError(@NonNull Throwable error) {
// Fatal flow error — log error.getMessage() for details
IncodeWelcome.getInstance().deleteUserLocalData();
}
Warning
Always call deleteUserLocalData()
Call IncodeWelcome.getInstance().deleteUserLocalData() in onError(), onSuccess(), and onUserCancelled() to ensure local session data is cleaned up regardless of how the flow exits.
Unlike the iOS SDK, Android does not use a typed error enum for fatal flow errors. The Throwable message is the primary source of diagnostic information.
Module result codes
Every module result object includes a resultCode property of type ResultCode. This indicates the high-level outcome of the module:
| Value | Description |
|---|---|
SUCCESS |
The module completed successfully. |
ERROR |
The module encountered an error. Check the result object for additional details. |
USER_CANCELLED |
The user cancelled the module. |
EMULATOR_DETECTED |
The module was running on an emulator. After a 2-second delay, the module returns this code automatically. Remove setTestModeEnabled(true) before production builds. |
For ID Scan specifically, emulator detection is returned as IdResults.RESULT_EMULATOR_DETECTED on the frontIdResult and backIdResult properties rather than via ResultCode.
Configuration errors
FlowConfig.Builder.build() throws ModuleConfigurationException synchronously if the flow configuration violates module rules; for example, if mandatory modules are omitted or ordering dependencies are violated. Catch this at build time:
try {
val flowConfig = FlowConfig.Builder()
.addID(IdScan.Builder().build())
.addSelfieScan(SelfieScan.Builder().build())
.addFaceMatch()
.build()
} catch (e: ModuleConfigurationException) {
// Invalid flow configuration — fix before running
Log.e("Incode", "Flow config error: ${e.message}")
}
try {
FlowConfig flowConfig = new FlowConfig.Builder()
.addID(new IdScan.Builder().build())
.addSelfieScan(new SelfieScan.Builder().build())
.addFaceMatch()
.build();
} catch (ModuleConfigurationException e) {
// Invalid flow configuration — fix before running
Log.e("Incode", "Flow config error: " + e.getMessage());
}
This is an Android-specific error type with no direct equivalent in the iOS or Web SDKs.
Delayed onboarding sync errors
When syncing offline (delayed) onboardings, errors are delivered via a dedicated listener:
IncodeWelcome.getInstance().syncDelayedOnboardings(object : SyncDelayedOnboardingListener {
override fun onError(error: DelayedOnboardingSyncError) {
// Handle sync error
}
})
Emulator behavior
On Android emulators, camera-dependent modules (ID Scan, Selfie Scan, Face Match, Document Scan, Video Selfie) show a black screen for 2 seconds then return ResultCode.EMULATOR_DETECTED automatically. Remove setTestModeEnabled(true) before building for production.
Flutter, React Native, and Xamarin
{/* REVIEW: Verify that Flutter, React Native, and Xamarin wrap the native SDKs before publishing this section. Replace with platform-specific detail if the error surface differs meaningfully. See review note 6. */}
The Flutter, React Native, and Xamarin SDKs wrap the native iOS and Android SDKs. Error handling in these platforms mirrors the underlying native platform:
- On iOS devices, errors follow the iOS SDK model described above: typed
IncdFlowErrorcases delivered via delegate callbacks, with per-module result error enums. - On Android devices, errors follow the Android SDK model:
Throwableerrors via listener callbacks, withResultCodeon module results.
Refer to each platform's integration guide for the platform-specific callback and listener signatures used to receive these errors.
Warning
Platform-specific error documentation
Detailed error handling documentation for Flutter, React Native, and Xamarin is coming soon. Contact your Incode customer success manager or refer to the native platform sections above in the meantime.