> For the complete documentation index, see [llms.txt](https://docs.keyless.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.keyless.io/consumer/mobile-sdk-reference/error-handling.md).

# Error handling

The Keyless SDK uses three classes of errors, each error has an error code and an error message. Errors follow 3 main categories:

* [**User errors**](https://docs.keyless.io/consumer/mobile-sdk-reference/error-handling#user-errors): triggered by unintended or suspicious user behavior.
  * `30_000` and above.
* [**Integration errors**](https://docs.keyless.io/consumer/mobile-sdk-reference/error-handling#integration-errors): triggered by a KeylessSDK integration misconfiguration.
  * These span from `20_000` to `30_000`.
* [**Internal errors**](https://docs.keyless.io/consumer/mobile-sdk-reference/error-handling#internal-errors): triggered by Keyless internals.
  * All errors below `20_000`

If you're implementing the Keyless SDK, you should handle errors coming from the SDK since the error message is not intended for end users.

## User errors

User errors have code that are `30_000` and above.

{% hint style="warning" %}
Note that many of these errors are predictions only, when writing messages for your users it's often best to assume positive intent. For example, suggest that they make sure that nothing is obstructing the camera instead of suggesting that they are attempting to spoof.
{% endhint %}

<table><thead><tr><th width="138">Error</th><th width="99">Code</th><th>Description</th><th>Notes /Recommendation</th></tr></thead><tbody><tr><td>Spoofing</td><td><code>30000</code></td><td>The user genuine presence cannot be established.</td><td>The user might be placing a picture or a video in front of the camera. Our system is probabilistic, upon failure, advise users to ensure their face is well-lit and fully visible before retrying. The user can utilize live feedback during the camera scan for real-time guidance.</td></tr><tr><td>Timeout</td><td><code>30001</code></td><td>The face could not be recognized before the specified timed out.<br><br><strong>Note:</strong> This error is no longer returned from SDK version <code>5.0.1</code> and above given the Liveness timeout feature was deprecated. <a href="https://docs.keyless.io/consumer/mobile-sdk-changelog/changelog#id-5.0.1">See Changelog for details</a>.</td><td>The user can retry placing the face in front of the camera as soon as the camera opens.</td></tr><tr><td>Mask detected</td><td><code>30002</code></td><td>The user might be wearing a mask, or there might be something hiding their face.<br><strong>Note:</strong> mask detected will be part of live feedback and no longer returned as an error from SDK version 4.8.0 and above.</td><td>It is very likely that there is some occlusion on the face. We advise to retry and utilize the live feedback during the camera scan for real-time guidance.</td></tr><tr><td>User cancelled</td><td><code>30003</code></td><td>The user manually cancelled the face recognition or image processing.</td><td>This occurs when the user cancels the face scan via the back button. We recommend either suppressing the error (the event is logged internally for debugging) or prompting the user to retry in case the cancellation was accidental.</td></tr><tr><td>Face not matching</td><td><code>30004</code></td><td>The face of the user in front of the camera does not match the face currently enrolled with Keyless.</td><td>This indicates a biometric mismatch: a face was detected, but it does not match the enrolled profile. We recommend prompting a retry or advising the user to verify they have selected the correct account.</td></tr><tr><td>No network connection</td><td><code>30005</code></td><td>The device appears to be offline.</td><td>This error indicates a loss of network connectivity. We recommend prompting the user to verify their internet connection and restart the scan. If the issue persists, advise them to wait a few minutes before retrying.</td></tr><tr><td>Device tampered</td><td><code>30006</code></td><td><p>The device seems tampered and could have been rooted or jailbroken. </p><p><strong>Note</strong>: this error has been superseded by the <a href="https://docs.keyless.io/consumer/mobile-sdk-guide/getting-started#installation">runtime application self protection</a>.</p></td><td>This signifies that the device is likely rooted/jailbroken. Advise the user to try on another device. If not possible reach out to Keyless support for further guidance.</td></tr><tr><td>User lockout</td><td><code>30007</code></td><td><p>The user is temporarily locked out of Keyless after too many failed authentication attempts. </p><p><strong>Note:</strong> in case of a <a href="https://docs.keyless.io/consumer/mobile-sdk-use-cases/guide-account-recovery/new-device-activation">new device activation</a> a different lockout code <code>523</code> is returned.</p></td><td>We advise the user to wait for the indicated time. We advise to expose to the user a functionality to check check the remaining time from the app before retrying. The customer can use the APIs in the <a href="https://docs.keyless.io/consumer/mobile-sdk-guide/user-and-device-management#lockout-management">lockout management</a> section.</td></tr><tr><td>Rejected</td><td><code>30008</code></td><td>Keyless did not manage to recognize the user but does not suspect any spoofing attempt.</td><td>In this case the model is not providing further hints on the issue. We advise users to utilize the live feedback during the camera scan for real-time guidance.</td></tr><tr><td>Camera denied</td><td><code>30009</code></td><td>The user denied camera permission.</td><td>We advise the customer app to ensure camera permissions are granted. If needed prepare an informative screen on why the camera permissions are necessary to perform the face scan.</td></tr></tbody></table>

## Integration errors

Integration errors have codes that span from `20_000` to `30_000`.

Integration errors can be solved by making sure you are not misusing the API surface of the SDK. You can solve it by reading the error message and addressing the issue. If errors persist, please keep the error code, error message and stacktrace and contact us.

<table><thead><tr><th width="138">Error</th><th width="99">Code</th><th>Description</th><th>Notes / Recommendation</th></tr></thead><tbody><tr><td><p><em>Legacy</em> </p><p>SDK configure failed</p></td><td><code>20005</code></td><td>This should no longer be returned and has been replaced by the error <code>20010</code></td><td>Refer to <code>20010</code></td></tr><tr><td>SDK configure failed</td><td><code>20010</code></td><td><p>There was an error when calling Keyless.configure. </p><p><strong>Note</strong>: from SDK version 6.0.0 and above the configuration error will be more fine grained; check the error message to understand the actual issue.</p></td><td>This error typically occurs due to misconfigured tenant feature flags or a failure to reach the feature flag service during SDK setup. In rare cases, it may also be caused by insufficient device storage preventing the SDK from writing necessary local files.</td></tr><tr><td>Liveness Environment Aware</td><td><code>20021</code></td><td><p>The device does not meet the requirements for environment-aware liveness detection. </p><p><strong>Note</strong>: from SDK version 6.0.0 and above the liveness environment aware check will be managed internally in the SDK.</p></td><td>We advise the customer to turn-off the liveness environment aware feature if this creates frictions for the users. </td></tr></tbody></table>

## Internal errors

All errors below `20_000` are classed as internal errors in that they relate to the response from our Core platform. Typically internal errors require investigation from Keyless support, however we've highlighted some of the more common ones here where action may be taken by the integrator.

<table data-full-width="true"><thead><tr><th>Error</th><th width="73.115478515625">Code</th><th width="336.81585693359375">Description</th><th>Notes / Recommendation</th></tr></thead><tbody><tr><td><code>PROTOCOL_INVALID_MESSAGE</code></td><td><code>507</code></td><td>client-server mismatch suggesting an issue in communicating with the Core backend.</td><td>This could be due the network dropping, or an outdated (unsupported) core client trying to contact the latest core backend.</td></tr><tr><td><code>PROTOCOL_FAILED_TO_AUTHENTICATE_USER</code></td><td><code>512</code></td><td>The given user selfie was did not meet the threshold of an approved match - in simple terms we were not confident enough that this was the same face as the one registered to this Keyless ID</td><td><p>In most cases we advise our customers to advise positive intent and request that the user retries reminding them to make sure they are in a well lit environment, their face is centred and they do not anything covering their face.</p><p>Of course integrators should consider that this error code is triggered because the person attempting to authenticate is not the same person that is registered and handle accordingly.</p></td></tr><tr><td><code>PROTOCOL_BAD_REQUEST</code></td><td><code>537</code></td><td>Core backend returned a 400 HTTP status error.</td><td>We advise to retry the operation. If the issue persists prompt the user to wait a few minutes before retrying. Lastly, if the user still sees the same error we advise to enroll the user anew.</td></tr><tr><td><code>PROTOCOL_MAX_NUMBER_OF_DEVICES_REACHED</code></td><td><code>539</code></td><td><p>The maximum number of devices in this has been reached. Note a maximum of 50 devices per Keyless ID is allowed.<br><br>Note also that these do not all necessarily represent physical devices of a user (they could be backup or temporary client states that can be use to bind future devices for example).</p><p><br></p></td><td><p>Revoke some of the existing states/devices in order to generate new ones.<br></p><p>For SaaS customers this can be done either manually via the <a href="https://dash.keyless.io/">Keyless dashboard</a> or programmatically via <a href="https://docs.keyless.io/consumer/server-api/devices#delete-users-userid-devices-publicsigningkey">the Server API</a>.</p></td></tr><tr><td><code>NET_CONNECTION_FAILED</code></td><td><code>1004</code></td><td>Network unavailable or server response unexpected.</td><td>If networking is available refer to <code>507</code>.</td></tr><tr><td><code>CLIENT_INVALID_INPUT</code></td><td><code>1134</code></td><td>Incorrect or invalid parameter passed to a core function.</td><td>This may be solved by re-running the Keyless enrollment or authentication request. Alternatively the SDK and core client sync may be in a corrupted state.</td></tr><tr><td><code>CLIENT_UNKNOWN_ERROR</code></td><td><code>1142</code></td><td>The local state of the Keyless SDK - core client - might not be in sync with the core backend.</td><td>This may happen also in case the device time is not set correctly. We advise to ensure the device is genuine and has a correct time setting, If the issue persists, we advise to enroll the user anew.</td></tr></tbody></table>

If errors in this range persist and are unclear, please keep the error code, error message and stacktrace and contact us. If possible we'd also recommend enabling [Keyless logging](https://docs.keyless.io/consumer/mobile-sdk-reference/logging) at `TRACE` level and sending these logs to us to further speed up the Keyless investigation.

## Anti-Inject errors

The errors in the following table apply to Keyless Anti-Inject variant only.

<table><thead><tr><th width="138">Error</th><th width="99">Code</th><th>Description</th></tr></thead><tbody><tr><td>Anti-inject initialization failed</td><td><code>40000</code></td><td><em>Keyless Anti-Inject variant only.</em> The initialization of the Anti-Inject failed for an internal error.</td></tr><tr><td>License expired</td><td><code>40001</code></td><td><em>Keyless Anti-Inject variant only.</em> The license "configFile.tak" is expired, contact Keyless team to receive an updated license.</td></tr><tr><td>Device might not be genuine.<br></td><td><code>40002</code></td><td><em>Keyless Anti-Inject variant only.</em> We have detected signals that this device may be compromised or may not be genuine. This error prevents a user from continuing and it won't be possible to enroll or authenticate.</td></tr></tbody></table>

## Examples

{% tabs %}
{% tab title="Android" %}

```kotlin
val configuration = Keyless.AuthenticationConfiguration()

Keyless.authenticate(
    authenticationConfiguration = configuration,
    onCompletion = { result ->
        when (result) {
            is Keyless.KeylessResult.Success -> {
                Log.d("IntegratorActivity ", "Authenticate success")
            }
            is Keyless.KeylessResult.Failure -> {
                when (result.error) {
                    is KeylessUserError.FaceNotMatching -> Log.d("IntegratorActivity ", "Face not matching")
                    is KeylessUserError.MaskDetected -> Log.d("IntegratorActivity ", "Mask detected")
                    is KeylessUserError.Spoofing -> Log.d("IntegratorActivity ", "Spoofing detected")
                    is KeylessUserError.Timeout -> Log.d("IntegratorActivity ", "The operation timed out")
                    is KeylessUserError.UserCancelled -> Log.d("IntegratorActivity ", "The user cancelled the operation")
                    is KeylessUserError.NoNetworkConnection -> Log.d("IntegratorActivity ", "No network connection available")
                    is KeylessUserError.Lockout -> Log.d("IntegratorActivity ", "Your account is temporarily locked")
                    else -> {
                        Log.d("IntegratorActivity ", "Authenticate failure")

                        val errorCode = result.error.code
                        val errorMessage = result.error.message
                        val errorCause = result.error.cause?.printStackTrace()
                        // here you could display a generic error popup with the error code
                    }
                }
            }
        }
    }
)
```

{% endtab %}

{% tab title="iOS" %}

```swift
let configuration = Keyless.AuthenticationConfiguration.builder.build()

Keyless.authenticate(authenticationConfiguration: configuration) { result in
    switch result {
    case .success(let authenticationSuccess):
        print("authenticationDidFinish:  \(authenticationSuccess.token)")
    case .failure(let error):
        switch error.kind {
        case .userError(let userError):
            switch userError {
            case .faceNotMatching:
                print("Face not matching")
            case .maskDetected:
                print("Mask detected")
            case .spoofing:
                print("Spoofing detected")
            case .timeout:
                print("The operation timed out")
            case .userCancelled:
                print("The user cancelled the operation")
            case .noNetworkConnection:
                print("No network connection available")
            case .lockout:
                print("Your account is temporarily locked")
            }
        default:
            let code = error.code
            let message = error.message
            // here you could display a generic error popup with the error code
        }
    }
}
```

{% endtab %}

{% tab title="Flutter" %}

```dart
import 'package:keyless_flutter_sdk/keyless.dart';
import 'package:keyless_flutter_sdk/models/configurations/authentication_configuration.dart';

final configuration = BiomAuthConfig();

try {
  final result = await Keyless.instance.authenticate(configuration);
  print("Authentication successful");
} catch (error) {
  if (error is KeylessError) {
    switch (error.errorType) {
      case KeylessErrorType.user:
        if (error.code == KeylessErrorCase.faceNotMatching.code) {
          print("Face not matching");
        } else if (error.code == KeylessErrorCase.maskDetected.code) {
          print("Mask detected");
        } else if (error.code == KeylessErrorCase.spoofing.code) {
          print("Spoofing detected");
        } else if (error.code == KeylessErrorCase.timeout.code) {
          print("The operation timed out");
        } else if (error.code == KeylessErrorCase.userCancelled.code) {
          print("The user cancelled the operation");
        } else if (error.code == KeylessErrorCase.noNetworkConnection.code) {
          print("No network connection available");
        } else if (error.code == KeylessErrorCase.deviceTampered.code) {
          print("Device security check failed");
        } else if (error.code == KeylessErrorCase.lockout.code) {
          print("Your account is temporarily locked");
        }
        break;
      default:
        // Handle internal or integration errors
        print("Authentication failed: ${error.message} (Code: ${error.code})");
        // Here you could display a generic error popup with the error code
    }
  }
}
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.keyless.io/consumer/mobile-sdk-reference/error-handling.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
