> ## Documentation Index
> Fetch the complete documentation index at: https://docs.frankieone.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Scenarios & Non-Positive States

> Complete reference for all error scenarios, failed states, and non-positive outcomes across OneSDK modules

## Overview

This page documents all non-positive scenarios across the OneSDK, including error events, failed verification states, timeouts, network issues, and recoverable conditions. Understanding these scenarios helps you build robust error handling and provide better user experiences.

<Note>
  For API error codes, see [API Error Codes](/docs/reference/error-codes). This page focuses on SDK-level errors and verification outcomes.
</Note>

***

## Global SDK Errors

### Network Events

#### `network_offline`

**Module:** SDK Context (Global)

**Description:** Emitted when network connection is lost.

**Example:**

```javascript theme={null}
oneSDKInstance.on('network_offline', () => {
  showBanner('You are offline. Verification will resume when connection is restored.');
});
```

**Recovery Steps:**

* Display offline indicator to user
* Queue pending operations
* Listen for `network_online` event to resume

***

#### `network_online`

**Module:** SDK Context (Global)

**Description:** Emitted when network connection is restored after being offline.

**Example:**

```javascript theme={null}
oneSDKInstance.on('network_online', () => {
  showBanner('Connection restored. Resuming verification...');
  resumePendingOperations();
});
```

***

### Error Event

**Event:** `error`

**Available On:** All modules (SDK context, Individual, IDV, OCR, Biometrics, Device, Form)

**Description:** Emitted when an error occurs in the SDK or any module.

**Error Structure:**

```typescript theme={null}
{
  message: string;      // Human-readable error description
  name: string;         // Error type identifier (empty string if not set)
  payload: unknown;     // Additional error context
}
```

**Example:**

```javascript theme={null}
oneSDKInstance.on('error', (error) => {
  console.error('SDK Error:', {
    message: error.message,
    name: error.name,
    payload: error.payload
  });

  showGenericError(error.message);
});
```

#### Known `error.name` Values

Some errors set a specific `name` value you can check programmatically. Many errors do not set `name` — for those, inspect `error.message` instead.

**Form Module:**

| `error.name`                        | Description                                                                          |
| ----------------------------------- | ------------------------------------------------------------------------------------ |
| `FORM:CONFIGURATION_MISSING`        | Form recipe is missing a provider name. Check your OneSDK form recipe configuration. |
| `FORM-LEGACY:CONFIGURATION_MISSING` | Legacy form provider is missing the required `version` field.                        |

**Individual Module:**

| `error.name` | Description                                                                                                                   |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------- |
| `SUBMIT`     | A submit retry is in progress. Fires between retry attempts (not on final failure). `error.message` includes the retry delay. |

**IDV / OCR / Biometrics — IDVerse provider:**

| `error.name`                    | Description                                                                      |
| ------------------------------- | -------------------------------------------------------------------------------- |
| `IDVerseUnableToCreateSession`  | IDVerse backend could not create a session (backend error code `IDVERSE:001`).   |
| `IDVerseTransactionClosed`      | IDVerse session was closed before completion (backend error code `IDVERSE:002`). |
| `IDVerseTransactionFailed`      | IDVerse transaction failed during verification.                                  |
| `IDVerseScanFailed`             | IDVerse scan exceeded the maximum retry count.                                   |
| `IDVerseScanFailedAndDestroyed` | IDVerse scan failed and the SDK instance was destroyed.                          |
| `IDVerseFatalError`             | IDVerse reported a fatal internal error.                                         |
| `IDVerseErrorOnStart`           | IDVerse OCR or face scan failed to start.                                        |

**IDV — Truuth provider:**

| `error.name`                        | Description                                                                             |
| ----------------------------------- | --------------------------------------------------------------------------------------- |
| `window is undefined`               | Truuth SDK requires a browser environment — `window` is not defined (e.g. SSR context). |
| `Failed to install TruuthKYCWebSdk` | Truuth SDK failed to install.                                                           |

#### Errors Without a `name`

Most errors emit with an empty `name`. Use `error.message` to identify them:

| Module                          | Scenario                                         | `error.message`                                                                                                                                                                                                     |
| ------------------------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| IDV                             | Provider offline (`PROVIDER_OFFLINE` status)     | `"Provider is offline. Either switch providers and restart or skip IDV temporarily."`                                                                                                                               |
| IDV                             | Consent not provided (`AWAITING_CONSENT` status) | `"Consent was not provided to the IDV Api. We can't continue."`                                                                                                                                                     |
| IDV / OCR / Biometrics          | Session creation network / HTTP error            | The HTTP error message. `error.name` will be set if the backend returned a known IDVerse error code (see IDVerse named errors above); otherwise empty. `error.payload.issues` contains any issues from the backend. |
| IDV / OCR / Biometrics / Device | Vendor SDK failed to load                        | `"Vendor sdk '{vendorName}' failed loading"` where `vendorName` is the configured provider. This fires alongside `vendor_sdk_failed_loading` — prefer handling that event instead.                                  |
| IDV                             | Sumsub failed to launch                          | `"Failed to launch IDV vendor 'Sumsub'"`                                                                                                                                                                            |
| IDV                             | Mount failed (async error during mount)          | Runtime error message from the caught exception                                                                                                                                                                     |
| IDV                             | Mastercard reported an error                     | `error.message` is `msg` from the Mastercard iframe postMessage payload — content is determined by Mastercard                                                                                                       |
| OCR                             | OCR results timed out after two polling attempts | `"Two attempts to get OCR results failed with an unresolved 'AWAITING_DOCUMENT_OCR'."`                                                                                                                              |
| OCR                             | Provider offline                                 | `"Provider is offline. Either switch providers and restart or skip OCR temporarily."`                                                                                                                               |
| OCR                             | Sumsub failed to launch                          | `"Failed to launch OCR vendor 'Sumsub'"`                                                                                                                                                                            |
| Biometrics                      | Generic vendor error                             | `"Error from vendor SDK '{provider}'"` where `{provider}` is the configured provider name                                                                                                                           |
| Biometrics                      | Biometrics submission error                      | `"Error from biometrics submission"`                                                                                                                                                                                |
| Biometrics                      | Provider offline                                 | `"Error initialising biometrics check with '{provider}'. The service doesn't appear to be available."`                                                                                                              |
| Biometrics                      | OCR not run before biometrics                    | `"Error initialising biometrics check with '{provider}'. You might need to run the 'ocr' component before running 'biometrics'."`                                                                                   |
| Biometrics                      | Selfie not captured before biometrics            | `"Error initialising biometrics check with '{provider}'. You might need to capture selfie before running 'biometrics'."`                                                                                            |
| Biometrics                      | Sumsub failed to launch                          | `"Failed to launch Biometrics vendor 'Sumsub'"`                                                                                                                                                                     |
| Individual                      | Submit failed (final attempt, retries exhausted) | `error.response?.data?.message` from the API response body, or the raw network error message if no response body                                                                                                    |
| Individual                      | Entity details failed to load (`search()`)       | `"Critical error! There was an issue loading the individual's details. Is the entityId ({entityId}) used to create this session valid?"` where `{entityId}` is the actual entity ID                                 |
| IDV — Daon provider             | Unexpected error received from the Daon iframe   | `"DAON_ERROR"`                                                                                                                                                                                                      |
| IDV — Daon provider             | Timeout from the Daon TrustX iframe              | `"DAON_TIMEOUT"`                                                                                                                                                                                                    |

**Recovery Steps:**

* Check `error.name` first if set; fall back to parsing `error.message`
* Use `error.payload` for additional context (e.g. HTTP response body, issues array)
* For session creation failures, `session_data_failed_loading` fires alongside `error` and includes the HTTP `code` and `message`
* For vendor SDK load failures, handle `vendor_sdk_failed_loading` directly — it carries the same information in a structured format

***

### Warning Event

**Event:** `warning`

**Module:** All modules

**Description:** Emitted for non-critical warnings that don't prevent operation but should be noted.

**Structure:**

```typescript theme={null}
{
  message: string;
  payload?: unknown;
}
```

**Example:**

```javascript theme={null}
oneSDKInstance.on('warning', (warning) => {
  console.warn('SDK Warning:', warning.message);
});
```

***

### Initialization Failures

**Scenario:** SDK initialization fails with Promise rejection

**Causes:**

* Invalid or expired session token
* Configuration error
* Network connectivity issues
* CSP policy blocking SDK resources

**Example:**

```javascript theme={null}
try {
  const sdk = await OneSdk({
    mode: 'production',
    session: { token: sessionToken }
  });
} catch (error) {
  console.error('Initialization failed:', error);

  // Generate fresh token and retry
  const newToken = await fetchNewTokenFromBackend();
  const sdk = await OneSdk({
    mode: 'production',
    session: { token: newToken }
  });
}
```

**Recovery Steps:**

1. Wrap initialization in try-catch
2. Generate fresh token from backend
3. Retry initialization with new token
4. Check browser console for CSP errors

***

### CSP (Content Security Policy) Errors

**Scenario:** Browser blocks OneSDK resources due to CSP policy

**Symptoms:** Browser console shows CSP violations when loading or running the SDK. Blocked resources may originate from FrankieOne's own domains or from third-party vendor SDKs (Onfido, Sumsub, Sardine, ThreatMetrix, etc.).

**Recovery Steps:**

Update your CSP headers based on what is being blocked:

| Blocked operation             | CSP directive to update |
| ----------------------------- | ----------------------- |
| Loading SDK scripts           | `script-src`            |
| API connections / XHR / fetch | `connect-src`           |
| Loading images from provider  | `img-src`               |
| Loading provider fonts        | `font-src`              |
| Vendor iframes (e.g. Sumsub)  | `frame-src`             |

Check the browser console for the specific blocked URL, then add the relevant domain to the appropriate directive. Both FrankieOne's domains and your chosen vendor's domains may need to be allowlisted.

```
Content-Security-Policy:
  script-src 'self' https://*.frankieone.com [vendor-domains];
  connect-src 'self' https://*.frankieone.com [vendor-domains];
  img-src 'self' https://*.frankieone.com [vendor-domains] data:;
```

Replace `[vendor-domains]` with the actual domains used by your chosen provider. See [Vendor CSP Settings](/docs/sdk-reference/vendor-customizations#content-security-policy-csp-settings) for vendor-specific domain lists.

***

## Session Management Errors

### Session Expiration

**Method:** `getExpirationEpoch()` returns timestamp in past

**Description:** User's session token has expired before verification completion.

**Example:**

```javascript theme={null}
const expirationEpoch = sdk.session.getExpirationEpoch();
const now = Date.now();

if (expirationEpoch && expirationEpoch * 1000 < now) {
  // Session expired — user must start over with a new token from your backend
  console.log('Session expired');
  showMessage('Your session has expired. Please start over.');
}
```

**Recovery Steps:**

1. Call `getExpirationEpoch()` to check expiration
2. Warn users when less than 5 minutes remaining
3. If the session expires mid-flow, the user must start over — there is no refresh token mechanism
4. Reinitialize SDK with a fresh token obtained from your backend

<Warning>
  There is no token refresh mechanism in OneSDK. If a session token expires while the user is mid-verification, you must generate a new token from your backend and reinitialize the SDK. Instruct users to start the verification flow again.
</Warning>

***

### Missing Entity ID

**Method:** `getEntityId()` returns `null`

**Description:** Session hasn't been associated with an entity yet.

**Example:**

```javascript theme={null}
const entityId = sdk.session.getEntityId();

if (!entityId) {
  console.log('Entity not yet created');
  // First-time user - proceed with data collection
} else {
  console.log('Existing entity:', entityId);
  // Returning user - load existing data
}
```

**Recovery Steps:**

* Always check for `null` before using entity ID
* Entity will be created after first data submission

***

## Individual Module (KYC) Errors

### Verification Status Failures

#### `failed`

**Property:** `CheckSummary.status.type`

**Description:** Verification failed automated checks.

**Example:**

```javascript theme={null}
const results = await individual.submit({ verify: true });

if (results.status.type === 'failed') {
  console.log('Verification failed');
  console.log('Failed checks:', results.checkResults.filter(c => c.result.type === 'failed'));

  // Check specific issues
  results.checkResults.forEach(check => {
    if (check.issues && check.issues.length > 0) {
      console.log(`Issues in ${check.type}:`, check.issues);
    }
  });
}
```

**Recovery Steps:**

1. Examine `checkResults` array for specific failed checks
2. Review `issues` object for detailed error information
3. Check `retryPages` for screens that can be retried
4. Allow user to correct data and resubmit

***

#### `fail_manual`

**Property:** `CheckSummary.status.type`

**Description:** Manually rejected after review by compliance staff.

**Recovery Steps:**

* Contact support for rejection reason
* May require completely new verification with different data
* User should be informed this is a final decision

***

#### `refer`

**Property:** `CheckSummary.status.type`

**Description:** Requires manual review; not automatically approved or denied.

**Example:**

```javascript theme={null}
if (results.status.type === 'refer') {
  showMessage('Your verification is under review. We\'ll notify you within 24-48 hours.');
}
```

**Recovery Steps:**

* Inform user that manual review is in progress
* Provide expected timeline (if known)
* Set up webhook to receive result notification
* Don't allow resubmission until review complete

***

#### `wait`

**Property:** `CheckSummary.status.type`

**Description:** Waiting for additional information from user.

**Example:**

```javascript theme={null}
if (results.status.type === 'wait') {
  // Check what actions are needed
  const actions = results.alertList.filter(alert => alert.type === 'action');

  actions.forEach(action => {
    console.log('Action required:', action.term);
    if (action.extra?.action) {
      console.log('Specific action:', action.extra.action);
    }
  });
}
```

**Recovery Steps:**

1. Check `alertList` for specific actions required
2. Implement handlers for common action types:
   * `additional_document_required` - Upload missing documents
   * `upload_proof_of_address` - Provide address verification
   * `document_expired` - Upload current document
3. Allow user to provide missing information
4. Resubmit verification after data provided

***

#### `unchecked`

**Property:** `CheckSummary.status.type`

**Description:** No checks have been run yet.

**Recovery Steps:**

* Call `submit({ verify: true })` or `runChecks()` to execute verification
* This is the initial state before any verification

***

#### `archived`

**Property:** `CheckSummary.status.type`

**Description:** Entity has been archived in the system.

**Recovery Steps:**

* Archiving is irreversible. You cannot unarchive an entity.
* Create a new entity and session to onboard the customer again.

***

#### `inactive`

**Property:** `CheckSummary.status.type`

**Description:** Entity has been deactivated.

**Recovery Steps:**

* Deactivation is irreversible. You cannot reactivate a deactivated entity.
* Create a new entity and session to onboard the customer again.

***

### Personal Information Validation Failures

**Properties:** `CheckSummary.personalChecks.*`

**Possible Values:**

* `true` - Field validated successfully
* `false` - Field failed validation
* `null` - Field was not checked

**Example:**

```javascript theme={null}
const { personalChecks } = results;

if (personalChecks.name === false) {
  showError('Name verification failed. Please check your name matches your ID.');
}

if (personalChecks.dateOfBirth === false) {
  showError('Date of birth verification failed.');
}

if (personalChecks.addresses) {
  Object.entries(personalChecks.addresses).forEach(([addressId, isValid]) => {
    if (isValid === false) {
      showError(`Address ${addressId} verification failed.`);
    }
  });
}
```

**Recovery Steps:**

1. Identify which specific fields failed (`=== false`)
2. Prompt user to correct the failed field
3. Resubmit verification with corrected data

***

### Risk Assessment Failures

**Properties:** `CheckSummary.risk.class`, `CheckSummary.risk.level`

**Risk Classes:** `low`, `medium`, `high`, `unacceptable`, `null`

**Risk Level:** Numeric value in the range 0–100. Lower numbers indicate lower risk.

<Note>
  The mapping between `risk.level` (numeric) and `risk.class` (categorical) depends on your risk configuration in FrankieOne and is not a fixed universal scale. When `risk.class` is `null`, `risk.level` is typically also absent. Contact your FrankieOne account team to understand your configured thresholds.
</Note>

**Example:**

```javascript theme={null}
const { risk } = results;

if (risk.class === 'high' || risk.class === 'unacceptable') {
  console.log('High risk detected, level:', risk.level);

  // Check for blocklist matches
  if (results.blocklistPotentialMatches.length > 0) {
    console.log('Blocklist matches found:', results.blocklistPotentialMatches);
  }

  // Check for duplicates
  if (results.duplicatePotentialMatches.length > 0) {
    console.log('Duplicate entities found:', results.duplicatePotentialMatches);
  }
}
```

**Recovery Steps:**

* `high` - May require manual review; check `blocklistPotentialMatches` and `duplicatePotentialMatches`
* `unacceptable` - Likely verification failure; escalate to compliance team
* Review `alertList` for specific risk factors

***

### Check Result Issues

**Property:** `CheckSummary.checkResults[].issues`

Each check result may include an `issues` array with `{ code, description }` entries identifying what failed. See [Individual Module — CheckSummary](/docs/sdk-reference/individual-module#checksummary-structure) for full structure.

**Recovery Steps:**

* Parse issues array to identify specific failures
* Display `issue.description` to user
* Use `issue.code` for programmatic handling

***

### Credit Header Issue

**Property:** `CheckSummary.issues.creditHeader`

**Accessor:** `individual.access('creditHeaderIssue')`

**Description:** A boolean flag set after `submit({ verify: true })` indicating the credit header check encountered an issue (e.g. the backend reported `hasCreditHeaderIssue` or a `creditHeaderIssueMessage`). Available on both the v1 and F2 API paths.

<Note>
  This is **not** a hard failure — it does not affect `CheckSummary.status.type`. Treat it as a data-quality signal about the credit header check rather than a blocking error.
</Note>

**Example:**

```javascript theme={null}
const results = await individual.submit({ verify: true });

if (results.issues?.creditHeader) {
  console.log('Credit header check flagged an issue — informational, not blocking');
}

// Or read it reactively via the accessor:
const hasCreditHeaderIssue = individual.access('creditHeaderIssue').getValue();
```

**Recovery Steps:**

* No user-facing action is required by default — the verification can still proceed based on `status.type`
* Optionally surface this to the user or your compliance team as an informational note
* Do not treat this flag alone as grounds to fail or block the applicant

***

### Alert List

#### Alert Types

All alerts in `alertList` have a `type` property that indicates severity:

| `type`    | Description                                                                   |
| --------- | ----------------------------------------------------------------------------- |
| `alert`   | High-priority issue requiring attention (fraud indicators, sanctions matches) |
| `action`  | User must take a specific action to proceed                                   |
| `warning` | Non-blocking concern that may affect approval                                 |
| `success` | Positive signal from a check                                                  |

The `term` property describes the specific topic of the alert. Known `term` values include:

| `term`      | Description                |
| ----------- | -------------------------- |
| `fraud`     | Fraud risk indicator       |
| `partial`   | Partial match found        |
| `media`     | Adverse media match        |
| `watchlist` | Watchlist match            |
| `sanction`  | Sanctions list match       |
| `blacklist` | Blocklist match            |
| `duplicate` | Potential duplicate entity |
| `risk`      | General risk indicator     |

<Note>
  The `term` value depends on the checks configured for your account. Handle unknown `term` values gracefully.
</Note>

#### Action Required

**Type:** `action`

**Property:** `CheckSummary.alertList`

**Common Actions:**

* `additional_document_required` - Upload supporting document
* `upload_proof_of_address` - Provide address verification document
* `document_expired` - Upload current/unexpired document

**Example:**

```javascript theme={null}
const actions = results.alertList.filter(alert => alert.type === 'action');

actions.forEach(action => {
  console.log('Action required:', action.term);

  if (action.extra?.action) {
    switch (action.extra.action) {
      case 'upload_proof_of_address':
        showUploadForm('Please upload proof of address');
        break;
      case 'additional_document_required':
        showUploadForm('Please upload additional document');
        break;
      case 'document_expired':
        showError('Your document has expired. Please upload a current document.');
        break;
    }
  }
});
```

**Recovery Steps:**

1. Filter `alertList` for `type === 'action'`
2. Implement handler for each action type
3. Collect required information from user
4. Resubmit verification

***

#### Warnings

**Type:** `warning`

**Example:**

```javascript theme={null}
const warnings = results.alertList.filter(alert => alert.type === 'warning');

warnings.forEach(warning => {
  console.warn('Warning:', warning.term);
});
```

**Recovery Steps:**

* Display warnings to user
* Assess if warnings prevent verification completion
* Some warnings may not block approval

***

### Duplicate Detection

#### Duplicate Potential Matches

**Property:** `CheckSummary.duplicatePotentialMatches`

**Description:** Entity may be a duplicate of an existing entity in the system. Each match includes `entityId`, `matchStrength` (0–1 confidence), and matched fields. See [Individual Module — CheckSummary](/docs/sdk-reference/individual-module#checksummary-structure) for full structure.

**Recovery Steps:**

1. Review duplicate matches manually
2. Verify if user already has an existing account
3. If legitimate duplicate, merge or link entities via backend API
4. If false positive, proceed with verification

***

#### Duplicate Blocklist Matches

**Property:** `CheckSummary.duplicateBlocklistMatches`

**Description:** Entity matches found across combined duplicate detection and blocklist/sanctions analysis. These represent entities flagged simultaneously in both checks and are high-priority alerts.

**Recovery Steps:**

* This is a critical alert
* Immediate investigation required
* Likely verification failure
* Escalate to compliance team

***

### Blocklist Matches

**Property:** `CheckSummary.blocklistPotentialMatches`

**Description:** Entity matched against blocklist/sanctions/AML databases.

**Example:**

```javascript theme={null}
if (results.blocklistPotentialMatches.length > 0) {
  console.log('BLOCKLIST MATCH DETECTED');

  results.blocklistPotentialMatches.forEach(match => {
    console.log('Matched entity:', match.entityId);
    console.log('Match confidence:', match.matchStrength);
    console.log('Matched personal fields:', match.matches.personal);
  });

  // Halt verification and escalate
  escalateToCompliance(results);
}
```

**Recovery Steps:**

* Stop verification process immediately
* Escalate to compliance/risk team
* Do NOT proceed with onboarding
* Perform manual investigation

***

### Retry Pages

**Property:** `CheckSummary.retryPages`

**Description:** Recommended pages where the user can retry their submission, so they don't need to go through the entire flow again. Can be empty if the backend cannot determine which pages should be retried.

**Example:**

```javascript theme={null}
if (results.status.type === 'failed') {
  if (results.retryPages.length > 0) {
    console.log('Recommended retry pages:', results.retryPages);
    // Navigate user to the first recommended retry page
    navigateToPage(results.retryPages[0].name);
  } else {
    // Backend couldn't determine retry pages - restart the full flow
    restartVerificationFlow();
  }
}
```

**Recovery Steps:**

* Navigate user to the recommended retry pages if provided
* User can correct specific data and resubmit
* If `retryPages` is empty, the user should restart the full verification flow

***

### `checkResults`, `checkTypes`, and `checkCounts`

For the relationship between `checkTypes`, `checkResults`, and `checkCounts`, see [Individual Module — CheckSummary](/docs/sdk-reference/individual-module#checksummary-structure).

Key points:

* `checkResults` depends on which checks are configured. A missing entry for a `checkType` typically means that check hasn't run yet.
* `checkCounts` lists data source names that verified each personal info field — not the check results themselves (those are in `personalChecks`).

***

### Submit Failures

**Method:** `submit()` or `submit({ verify: true })`

**Description:** Promise rejection on error.

**Causes:**

* Network error
* Validation error
* Server error
* Timeout

**Example:**

```javascript theme={null}
try {
  // retryOptions is NOT enabled by default — pass it explicitly to enable retry behavior
  const results = await individual.submit(
    { verify: true },
    {
      maxRetries: 3,
      backoffDelayMs: 1000
    }
  );
} catch (error) {
  console.error('Submit failed:', error);

  if (error.message.includes('network')) {
    showError('Network error. Please check your connection and try again.');
  } else if (error.message.includes('validation')) {
    showError('Please check your information and try again.');
  } else {
    showError('An error occurred. Please try again later.');
  }
}
```

**Recovery Steps:**

1. Pass `retryOptions` explicitly to enable automatic retries (not on by default)
2. Implement custom error handling in the `catch` block
3. Show user-friendly error messages
4. For persistent errors, contact support

***

### Accessor Usage

**Method:** `individual.access(fieldName)`

**Description:** Returns a reactive Accessor object for reading and writing individual entity fields. Use `access()` for reading or writing entity data within the module — you don't have direct access to the full raw entity data object.

**Accessor Interface:**

```typescript theme={null}
{
  getValue(): T;              // Read current value
  setValue(value: T): void;  // Write new value
  observable: Observable<T>; // Subscribe to reactive changes
}
```

Use `individual.search()` to refresh entity data from FrankieOne (fetches latest from backend). Use `individual.access()` for reading/writing the local entity state.

**Example:**

```javascript theme={null}
// Read
const name = individual.access('name').getValue();

// Write
individual.access('name').setValue({ givenName: 'John', familyName: 'Doe' });

// Subscribe to reactive changes
individual.access('name').observable.subscribe((newName) => {
  console.log('Name updated:', newName);
});
```

See [Individual Module - access()](/docs/sdk-reference/individual-module#access-fieldname) for the full list of available fields.

***

### Data Not Persisted

**Method:** `isPersisted()` returns `false`

**Description:** Changes haven't been submitted to the server yet.

**Example:**

```javascript theme={null}
if (!individual.isPersisted()) {
  showWarning('You have unsaved changes.');
}

// Before navigation
window.addEventListener('beforeunload', (e) => {
  if (!individual.isPersisted()) {
    e.preventDefault();
    e.returnValue = 'You have unsaved changes. Are you sure you want to leave?';
  }
});
```

**Recovery Steps:**

* Warn user before navigation
* Implement unsaved changes detection
* Prompt to save before leaving page

***

## Common Events: Vendor Modules

The following events are emitted by all vendor-based modules — IDV, OCR, Biometrics, and Device. See the respective module docs for full parameter details.

### `vendor_sdk_loaded`

Emitted when the third-party provider's SDK has been successfully loaded and is ready.

### `vendor_sdk_failed_loading`

Emitted when the provider's SDK fails to load. This is the primary signal that the module cannot function.

```javascript theme={null}
// Applies to: IDV, OCR, Biometrics, Device
module.on('vendor_sdk_failed_loading', ({ vendor, errorObject }) => {
  console.error(`Failed to load ${vendor} SDK:`, errorObject);
});
```

**Recovery Steps:**

* Check CSP headers allow the provider's domains
* Verify credentials (`clientID`, environment setting)
* Ensure the provider is configured for your account in FrankieOne
* Fall back to an alternative flow if possible

### `session_data_generated`

Emitted when a provider session has been created. Useful for logging or debugging.

### `session_data_failed_loading`

Emitted when session data fails to load from the provider. Usually indicates a backend configuration issue.

```javascript theme={null}
module.on('session_data_failed_loading', ({ code, message }) => {
  console.error('Session failed:', message);
  showError('Unable to start verification. Please try again.');
});
```

***

## IDV Module Errors

For `vendor_sdk_failed_loading`, `vendor_sdk_loaded`, `session_data_generated`, and `session_data_failed_loading`, see [Common Events: Vendor Modules](#common-events-vendor-modules).

### Status-to-Event Mapping

IDV statuses are delivered through different events depending on their nature:

| Status                  | Event            | Meaning                                                          |
| ----------------------- | ---------------- | ---------------------------------------------------------------- |
| `COMPLETE`              | `results`        | Verification completed successfully                              |
| `FAILED`                | `results`        | Verification failed                                              |
| `INCOMPLETE`            | `input_required` | Verification could not be completed due to incomplete user input |
| `DOCUMENTS_INVALID`     | `input_required` | Document provided was invalid or wrong type                      |
| `PROVIDER_OFFLINE`      | `error`          | The verification provider is unavailable                         |
| `AWAITING_CONSENT`      | `input_required` | User has not yet provided consent                                |
| `WAITING_SELFIE_UPLOAD` | `input_required` | Waiting for selfie capture                                       |
| `WAITING_DOC_UPLOAD`    | `input_required` | Waiting for document upload                                      |
| `INTERRUPTED`           | `input_required` | Verification flow was closed or interrupted                      |

<Note>
  The `results` event is only emitted with `COMPLETE` or `FAILED` status. All other statuses appear via `input_required`, `error`, or `processing` events. The Biometrics module uses the same `IDVStatus` enum — all statuses above apply to both IDV and Biometrics.
</Note>

### IDV Status Failures

#### `FAILED`

**Property:** `results.checkStatus`

**Example:**

```javascript theme={null}
idvFlow.on('results', (results) => {
  if (results.checkStatus === 'FAILED') {
    console.log('IDV verification failed');
    showFailureScreen();
  }
});
```

***

#### `INCOMPLETE`

**Description:** Verification could not be completed because of missing user input.

**Example:**

```javascript theme={null}
idvFlow.on('input_required', (info, status) => {
  if (status === 'INCOMPLETE') {
    showMessage('Verification incomplete. Please complete all required steps.');
    allowRetry();
  }
});
```

***

#### `DOCUMENTS_INVALID`

**Description:** Provided documents are invalid or of wrong type.

**Example:**

```javascript theme={null}
idvFlow.on('input_required', (info, status) => {
  if (status === 'DOCUMENTS_INVALID') {
    showError('Invalid document. Please select a different document type.');
    allowRetry();
  }
});
```

***

#### `PROVIDER_OFFLINE`

**Description:** IDV provider is offline/unavailable. This status surfaces as an `error` event.

**Recovery Steps:**

* Show unavailable message to user
* Retry after a delay
* If persistent, contact support

***

#### `AWAITING_CONSENT`

**Description:** Waiting for user consent to proceed.

**Example:**

```javascript theme={null}
idvFlow.on('input_required', (info, status) => {
  if (status === 'AWAITING_CONSENT') {
    showMessage('Please provide consent to continue verification.');
    allowRetry();
  }
});
```

***

#### `WAITING_SELFIE_UPLOAD`

**Description:** Waiting for selfie/biometric capture.

**Example:**

```javascript theme={null}
idvFlow.on('input_required', (info, status) => {
  if (status === 'WAITING_SELFIE_UPLOAD') {
    showMessage('Please capture your selfie to continue.');
    allowRetry();
  }
});
```

***

#### `WAITING_DOC_UPLOAD`

**Description:** Waiting for document upload.

**Example:**

```javascript theme={null}
idvFlow.on('input_required', (info, status) => {
  if (status === 'WAITING_DOC_UPLOAD') {
    showMessage('Please upload your document to continue.');
    allowRetry();
  }
});
```

***

#### `INTERRUPTED`

**Description:** Verification session was closed or interrupted by the user.

**Example:**

```javascript theme={null}
idvFlow.on('input_required', (info, status) => {
  if (status === 'INTERRUPTED') {
    // Remount the module to allow the user to resume
    showMessage('Verification was interrupted. Click retry to continue.');
    showRetryButton(() => idvFlow.mount('#idv-container'));
  }
});
```

***

### `input_required` — Semantics

**Event:** `input_required`

The `input_required` event is emitted when a check could not be completed due to user input — not because of a system failure. For example:

* The user uploaded the wrong document type
* The user closed the verification session before completing it
* Required captures (selfie, document) have not been provided

Retrying the step will always help resolve `input_required` — if the user closed the session, remounting the module allows them to continue. Whether to limit the number of retry attempts depends on your business process.

<Note>
  There is no way to differentiate between user abandonment (user deliberately closed the session) and session timeout within the SDK. Both emit `input_required` with `INTERRUPTED` status.
</Note>

**Example:**

```javascript theme={null}
idvFlow.on('input_required', (info, status) => {
  console.log('Input required:', status);

  // Show retry option — remounting the module resumes the flow
  showRetryButton(() => {
    idvFlow.unmount();
    idvFlow.mount('#idv-container');
  });
});
```

***

### Detection Failed

**Event:** `detection_failed`

**Example:**

```javascript theme={null}
idvFlow.on('detection_failed', ({ message, payload }) => {
  console.error('Detection failed:', message);
  console.error('Details:', payload);
  showError(message || 'Document capture failed. Please try again.');
});
```

**Recovery Steps:**

* Log error details from `payload`
* Show user-friendly error message
* Offer retry option

***

### Session Interrupted

**Event:** `session_interrupted`

**Provider:** Onfido-specific. Other providers do not emit this event — if you listen for it while using a non-Onfido provider, the handler will simply never fire.

**Example:**

```javascript theme={null}
// Only register this listener if using Onfido
idvFlow.on('session_interrupted', () => {
  console.log('User closed verification (Onfido)');
  saveProgress();
  showMessage('You can resume verification later.');
});
```

***

## OCR Module Errors

For `vendor_sdk_failed_loading`, `vendor_sdk_loaded`, `session_data_generated`, and `session_data_failed_loading`, see [Common Events: Vendor Modules](#common-events-vendor-modules).

<Note>
  **`isPreloaded()` in OCR:** The `isPreloaded()` method is not typically used in standard OCR integrations and can be safely ignored. Use events (`detection_complete`, `results`) to track document capture progress instead.
</Note>

### OCR Status Failures

#### `DOCUMENTS_INVALID`

**Example:**

```javascript theme={null}
ocrComponent.on('input_required', (info, status) => {
  if (status === 'AWAITING_DOCUMENT_UPLOAD_INVALID_TYPE') {
    showError('Invalid document type. Please select a different document.');
  }
});
```

***

#### `DOCUMENTS_UPLOAD_FAILED`

**Description:** Document upload failed during OCR processing.

**Recovery Steps:**

* Retry upload
* Check file size and format
* Verify network connection

***

#### `PROVIDER_OFFLINE`

**Description:** OCR provider is offline/unavailable.

**Recovery Steps:**

* Show provider unavailable message
* Retry after delay
* Fall back to manual data entry if available

***

#### `FAILED_FILE_SIZE`

**Description:** File size exceeds provider limit.

**Recovery Steps:**

* Show file size error
* Indicate maximum allowed size
* Ask user to provide smaller file or different format

***

#### `FAILED_FILE_FORMAT`

**Description:** Incorrect file format provided.

**Recovery Steps:**

* Show format error
* List accepted formats (JPG, PNG, PDF, etc.)
* Prompt user to select valid file

***

#### `INTERRUPTED`

**Description:** Capture flow was interrupted.

**Recovery Steps:**

* Allow user to restart capture\\

***

## Biometrics Module Errors

<Note>
  The Biometrics module uses the same `IDVStatus` enum as the IDV module. All statuses documented in the [IDV section](#idv-module-errors) (COMPLETE, FAILED, INCOMPLETE, DOCUMENTS\_INVALID, etc.) apply equally to Biometrics. There are no Biometrics-specific statuses.
</Note>

For `vendor_sdk_failed_loading`, `vendor_sdk_loaded`, `session_data_generated`, and `session_data_failed_loading`, see [Common Events: Vendor Modules](#common-events-vendor-modules).

***

### Session Data Failed Loading

**Event:** `session_data_failed_loading`

**Example:**

```javascript theme={null}
biometricsComponent.on('session_data_failed_loading', ({ code, message }) => {
  console.error('Session failed:', code, message);
  showError('Failed to start biometric verification. Please try again.');
});
```

***

### Input Required

**Event:** `input_required`

**Common Scenarios:**

* User closed verification session
* Session timeout occurred
* Waiting for selfie capture
* Camera permission denied

**Example:**

```javascript theme={null}
biometricsComponent.on('input_required', (info, status) => {
  console.log('Input required:', status);
  showMessage('Please complete the biometric capture to continue.');
  showRetryButton();
});
```

***

### Detection Failed

**Event:** `detection_failed`

**Example:**

```javascript theme={null}
biometricsComponent.on('detection_failed', ({ message }) => {
  console.error('Biometric capture failed:', message);
  showError(message || 'Face capture failed. Please try again.');
});
```

***

## Device Module Errors

For `vendor_sdk_failed_loading`, `vendor_sdk_loaded`, and `session_data_generated`, see [Common Events: Vendor Modules](#common-events-vendor-modules). For the Device module specifically, `vendor_sdk_failed_loading` means device fingerprinting is unavailable — you should fall back to proceeding without device data.

### Configuration Validation Errors

**Scenario:** Missing provider configuration

**Error Message:** "Your account is missing the environment configuration..."

**Recovery Steps:**

* Ensure `recipe.deviceCharacteristics.provider` is configured
* Verify `clientID` and `environment` are set and correct
* Contact FrankieOne support to verify the provider is enabled for your account

***

### Invalid Activity Type

**Scenario:** Invalid activity type provided to `start()`

**Error Message:** "Activity Type should be one of the following..."

**Valid Types:** `REGISTRATION`, `LOGIN`, `CRYPTO_DEPOSIT`, `CRYPTO_WITHDRAWAL`, `FIAT_DEPOSIT`, `FIAT_WITHDRAWAL`

**Example:**

```javascript theme={null}
// Incorrect
oneSdkInstance.component('device', { activityType: 'registration' }); // ✗ lowercase

// Correct
oneSdkInstance.component('device', { activityType: 'REGISTRATION' }); // ✓ uppercase
```

**Recovery Steps:**

* Use exact activity type string (case-sensitive)
* Verify spelling matches valid types

***

### Device Data Not Appearing in Dashboard

**Troubleshooting checklist:**

1. **Check environment** — Verify you are checking the correct environment (sandbox vs production) in the dashboard. A mismatch here is the most common cause.
2. **Check clientID** — Ensure the `clientID` matches what FrankieOne has configured for your account.
3. **Provider configured** — Confirm with your FrankieOne account team that the device fingerprinting provider is enabled and configured for your account before integrating.
4. **CSP blockage** — There is no direct client-side indicator that device data was blocked (other than the `vendor_sdk_failed_loading` event or CSP console errors). Check browser console for blocked requests.
5. **Success indicator** — Listen for the `device_characteristics_extracted` event to confirm device data was collected client-side. If this event fires, the data was captured successfully.
6. **Timeline** — How quickly device data appears in the dashboard depends on the vendor. Contact your FrankieOne account team for expected timing.

```javascript theme={null}
deviceComponent.on('device_characteristics_extracted', ({ deviceCharacteristics }) => {
  console.log('Device data collected — should appear in dashboard');
});
```

***

## Form Module Errors

### Screen Failed Events

**Event Pattern:** `form:{screen}:failed`

**Screens:** `welcome`, `start`, `consent`, `document`, `personal`, `review`, `loading`, `result`, `retry`, `doc_upload`, `required_document_upload`, `partial_document_upload`, `optional_document_upload`

**Example:**

```javascript theme={null}
formComponent.on('form:welcome:failed', (payload) => {
  console.error('Welcome screen failed to load');
  showError('Unable to start verification. Please refresh the page.');
});
```

**Recovery Steps:**

* Log error for investigation
* Show user-friendly error message
* Offer page refresh or retry
* Contact support if persists

***

### Review Events (`form:review:*`)

<Warning>
  `form:review:failed`, `form:review:success`, `form:review:partial`, and `form:review:pending` are **only emitted when [`customResult: true`](/docs/sdk-reference/form-module/configuration#page-configuration-types) is set in manual mode**. When `customResult` is `false` (the default), the form automatically navigates to and displays the configured result screen — no review events are emitted for you to handle.

  In OCR mode, the only review event is `form:review:ready`.

  See [`verify`](/docs/sdk-reference/form-module/configuration#page-configuration-types) and [`customResult`](/docs/sdk-reference/form-module/configuration#page-configuration-types) in the configuration reference.
</Warning>

Use `customResult: true` when you want to intercept the review outcome and handle navigation yourself (e.g., display a custom message or CTA):

#### `form:review:failed`

**Description:** Verification did not pass at review stage. Only emitted with `customResult: true`.

**Example:**

```javascript theme={null}
formComponent.on('form:review:failed', (payload) => {
  console.log('Verification failed');
  showCustomFailureUI();
});
```

***

#### `form:review:partial`

**Description:** Partial verification result. Only emitted with `customResult: true`.

**Example:**

```javascript theme={null}
formComponent.on('form:review:partial', (payload) => {
  showCustomPartialUI();
});
```

***

#### `form:review:pending`

**Description:** Result pending manual review. Only emitted with `customResult: true`.

**Example:**

```javascript theme={null}
formComponent.on('form:review:pending', (payload) => {
  showCustomPendingUI();
});
```

***

### Result Screen States

Result screen states are **display configurations** — not error events to handle. They control what message and CTA the built-in result screen shows. Set the state when initializing the RESULT screen to display the appropriate message to the user.

| State            | Description                                                   |
| ---------------- | ------------------------------------------------------------- |
| `SUCCESS`        | Verification passed                                           |
| `FAIL`           | Verification failed                                           |
| `PENDING`        | Verification pending manual review                            |
| `PARTIAL`        | Partial verification result                                   |
| `UPLOAD_FAIL`    | Document upload failed                                        |
| `PROVIDER_ERROR` | Provider encountered an error                                 |
| `TIMEOUT`        | The identity verification process took too long and timed out |

**Example — configure a failure result screen:**

```javascript theme={null}
const failResult = sdk.component('form', {
  name: 'RESULT',
  type: 'manual',
  state: 'FAIL',
  title: { label: 'Verification Failed' },
  descriptions: [{ label: 'We were unable to verify your information.' }]
});

failResult.on('form:result:loaded', () => {
  console.log('Failure screen displayed');
});
```

**Example — configure a timeout result screen:**

```javascript theme={null}
const timeoutResult = sdk.component('form', {
  name: 'RESULT',
  type: 'manual',
  state: 'TIMEOUT',
  title: { label: 'Your session has timed out' },
  descriptions: [{ label: 'The identity verification process took too long and has timed out. Please close this window and try again.' }]
});

timeoutResult.on('form:result:timeout', () => {
  console.log('Timeout screen displayed');
});
```

<Note>
  `TIMEOUT` uses its own icon and copy, distinct from `FAIL`. When the RESULT screen is configured with `state: 'TIMEOUT'`, the module emits a `form:result:timeout` event (in addition to the generic `form:result:loaded`) — listen for it if you want to handle the timeout case specifically, for example to log it separately from a hard verification failure.
</Note>

***

### Review Screen — Retry Configuration

When a form submission fails on the Review screen, the SDK has built-in retry behavior controlled by the Review screen's CTA (`cta`) configuration. This is **not** on the Retry screen.

**CTA Configuration (Review screen):**

```javascript theme={null}
{
  name: 'REVIEW',
  // ...
  cta: {
    label: 'Submit',
    timeoutRetryMiliseconds: 5000,  // Auto-retry delay ms — the form retries automatically after this
    retryAmount: 3,                 // Max retry attempts per submission
    retryAttemptString: 'Error submitting form. Give it a moment to retry.',
    maxRetryAttemptedString: 'Something went wrong, please contact customer service or try again.'
  }
}
```

* **`timeoutRetryMiliseconds`** — The form automatically retries submission after this delay. No user click needed.
* **`retryAmount`** — Maximum retry attempts. After exhausting retries, the user sees `maxRetryAttemptedString`.
* The retry counter is per submission, not persistent across sessions.
* No event is emitted per retry attempt.

### Retry Screen

**Screen:** `RETRY`

The Retry screen is specific to the eKYC flow — it is shown when verification fails and the user needs to re-enter data. It is not the same as the Review screen's submission retry.

**Example:**

```javascript theme={null}
const retry = sdk.component('form', {
  name: 'RETRY',
  type: 'manual'
});

retry.on('form:retry:loaded', (payload) => {
  console.log('User can correct data and retry eKYC');
});
```

***

## Best Practices

### 1. Always Handle Errors

```javascript theme={null}
// ✓ Good
oneSDKInstance.on('error', (error) => {
  logError(error);
  showUserFriendlyMessage(error);
});

// ✗ Bad - No error handler
oneSDKInstance.on('results', handleResults); // Missing error handler
```

***

### 2. Provide User-Friendly Messages

```javascript theme={null}
// ✓ Good
oneSDKInstance.on('error', (error) => {
  if (error.name === 'network_error') {
    showMessage('Please check your internet connection and try again.');
  } else {
    showMessage('Something went wrong. Please try again later.');
  }
});

// ✗ Bad - Technical jargon
oneSDKInstance.on('error', (error) => {
  alert(error.message); // May contain technical details
});
```

***

### 3. Check Status Before Acting

```javascript theme={null}
// ✓ Good
const results = await individual.submit({ verify: true });

switch (results.status.type) {
  case 'passed':
    navigateToSuccess();
    break;
  case 'failed':
    showFailureScreen(results);
    break;
  case 'refer':
    showPendingReview();
    break;
  case 'wait':
    showAdditionalInfoRequired(results.alertList);
    break;
  default:
    handleUnexpectedStatus(results.status.type);
}

// ✗ Bad - Only checking success
if (results.status.type === 'passed') {
  navigateToSuccess();
}
// Missing handling for all other states
```

***

### 4. Log Errors for Investigation

```javascript theme={null}
// ✓ Good
oneSDKInstance.on('error', (error) => {
  // Log to analytics/monitoring
  logToSentry({
    message: error.message,
    name: error.name,
    payload: error.payload,
    timestamp: Date.now()
  });

  // Show user-friendly message
  showError('An error occurred. Our team has been notified.');
});
```

***

### 5. Handle Network State

```javascript theme={null}
// ✓ Good
let isOnline = true;

oneSDKInstance.on('network_offline', () => {
  isOnline = false;
  showBanner('You are offline. Changes will be saved when connection is restored.');
  disableSubmitButton();
});

oneSDKInstance.on('network_online', () => {
  if (!isOnline) {
    isOnline = true;
    hideBanner();
    enableSubmitButton();
    retryPendingOperations();
  }
});
```

***

### 6. Validate Session Before Critical Operations

```javascript theme={null}
// ✓ Good
async function performVerification() {
  const expirationEpoch = sdk.session.getExpirationEpoch();
  const fiveMinutes = 5 * 60 * 1000;

  if (expirationEpoch && (expirationEpoch * 1000 - Date.now()) < fiveMinutes) {
    console.warn('Session expiring soon — request a new token from your backend');
    // There is no token refresh mechanism — must obtain new token from backend
    const newToken = await fetchNewTokenFromBackend();
    await reinitializeSDK(newToken);
  }

  // Proceed with verification
  const results = await individual.submit({ verify: true });
}
```

***

### 7. Ignore Telemetry Events in Production

Telemetry events (`telemetry`) are emitted internally by FrankieOne for diagnostic purposes. You do not need to listen to them in your application. Error handling should be done via the `error` event on each module, not via telemetry.

```javascript theme={null}
// ✗ Not needed in production
oneSDKInstance.on('telemetry', (event) => {
  console.log('Telemetry:', event); // Only useful for FrankieOne debugging
});
```

<Note>
  Before being included in `COMPONENT:INIT` / `COMPONENT:INIT:ERROR` (and other) telemetry events, sensitive values (tokens, API keys, secrets, `clientID`, etc.) are automatically redacted from the reported options and recipe data.
</Note>

***

## Provider Event Compatibility

Some events are provider-specific and will not fire when using a different provider. If your flow supports multiple providers, check the provider name before registering provider-specific event listeners.

| Event                       | Onfido | Sumsub | Incode | IDVerse | Daon |
| --------------------------- | ------ | ------ | ------ | ------- | ---- |
| `session_interrupted`       | ✓      | —      | —      | —       | —    |
| `detection_complete`        | ✓      | ✓      | ✓      | ✓       | ✓    |
| `vendor_event`              | ✓      | ✓      | ✓      | ✓       | ✓    |
| `vendor_sdk_loaded`         | ✓      | ✓      | ✓      | ✓       | ✓    |
| `vendor_sdk_failed_loading` | ✓      | ✓      | ✓      | ✓       | ✓    |

<Note>
  If you register a listener for `session_interrupted` while using Sumsub (or any non-Onfido provider), the handler will simply never fire. This is safe — it won't cause errors.
</Note>

***

## Document Selection

The Document Selection screen in the Form module is not deprecated and not tied to legacy flows. It is a standard part of the OCR and manual verification flows.

**Recommended patterns for document type selection:**

1. **Use OCR mode** — Specify document types in the `documents` array when creating the OCR component. The provider's built-in selector will guide the user.
2. **Hard code the document type** — If your flow requires a specific document type, specify it directly in configuration rather than letting the user choose.
3. **Use the provider's built-in selector** — Most IDV and OCR providers include their own document type selection UI.

***

## Vendor-Specific Errors

### Onfido

These errors are emitted via the `error` event when using Onfido as the IDV, OCR, or Biometrics provider. OneSDK surfaces these from Onfido's `onError` callback.

```javascript theme={null}
const idv = oneSdk.flow("idv");
idv.on('error', (error) => {
  console.error('Onfido error:', error);
});
```

| Error Code                | Description                                                | Recovery                                                                                                                           |
| ------------------------- | ---------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| `invalid_token`           | The Onfido IDV token is invalid.                           | Re-generate the IDV token by remounting the component.                                                                             |
| `expired_token`           | The Onfido IDV token has expired.                          | Re-generate the IDV token by remounting the component.                                                                             |
| `expired_trial`           | The Onfido trial period has expired.                       | This is unexpected in production. Contact FrankieOne support if you encounter this error.                                          |
| `geoblocked_request`      | The request is not allowed from the user's location.       | The end-user may be in a geoblocked region. Ask them to disable any active VPN, or contact FrankieOne for details.                 |
| `permissions_unavailable` | Unable to retrieve or access required user permissions.    | This may occur when the SDK is loaded within a webview or custom browser that restricts permissions. Try using a standard browser. |
| `unsupported`             | A module is not supported in the current environment.      | Check the OneSDK documentation for environment requirements, or contact FrankieOne support.                                        |
| `unsupported_feature`     | The requested feature is no longer supported.              | Check the OneSDK documentation for current feature availability, or contact FrankieOne support.                                    |
| `invalid_sdk_parameter`   | The SDK was initialized with invalid parameters.           | Review your Onfido provider configuration and correct any invalid parameters.                                                      |
| `unsupported_sdk_version` | The workflow is not supported by the current SDK version.  | Verify that your configured `sdkVersion` (e.g., `"12"` or `"14"`) is compatible with your workflow.                                |
| `no_camera`               | No camera is available and no other capture method exists. | The user has not granted camera access or their device does not have a camera. Prompt the user to enable camera permissions.       |
| `user_consent_denied`     | The user declined the consent prompt.                      | The user must provide consent to proceed. Display a message explaining why consent is required and allow them to retry.            |

<Note>
  For Onfido provider configuration and customization options, see [Vendor Customizations](/docs/sdk-reference/vendor-customizations).
</Note>

***

## Related

* [API Error Codes](/docs/reference/error-codes) - Backend API error codes
* [Event System](/docs/sdk-reference/events) - Complete event reference
* [Individual Module](/docs/sdk-reference/individual-module) - KYC module documentation
* [IDV Module](/docs/sdk-reference/idv-module) - IDV module documentation
* [Form Module](/docs/sdk-reference/form-module) - Form module documentation
