General reference

SDK Error Handling

{/* TECHNICAL REVIEW NOTES (not published by ReadMe)

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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 IncdFlowError cases delivered via delegate callbacks, with per-module result error enums.
  • On Android devices, errors follow the Android SDK model: Throwable errors via listener callbacks, with ResultCode on 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.


Was this page helpful?