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

# Handling Failures

Reference for Flutter Face Liveness failure handling. Learn CerttaLivenessFailure types, cause values, and how to map failures to clear user guidance.

{% hint style="warning" %}
This guide covers **caf\_sdk 2.4.0** and above (Certta DevX). For the previous `CafSdk` sequential flow, see the [legacy documentation](https://docs.caf.io/caf-sdk/flutter/getting-started-with-the-sdk).
{% endhint %}

When a liveness check fails, the SDK emits `CerttaLivenessEvent.failed` with a `CerttaLivenessFailure` object. This object contains a `cause` parameter (a stable code for mapping logic) and, for recognition failures, a `description` field with user-facing text.

Understanding and handling these fields is critical for providing clear, actionable feedback so users can correct the issue and try again.

### The failure models

Both capture-level and recognition-level failures share the same `CerttaLivenessFailure` type. Use `type` to distinguish them:

| `type`                   | Meaning                                                                                                                                            |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `ImageCaptureFailure`    | Image capture failed. No signed image (`response`) is available.                                                                                   |
| `FaceRecognitionFailure` | Capture succeeded, but recognition or authentication failed. `response` holds the signed payload. `description` provides a human-readable message. |

For `FaceRecognitionFailure`, `cause` is normalized to uppercase by the Flutter plugin. Use `cause` for conditional logic and `description` (or your own localized copy) for messages shown to end users.

{% hint style="warning" %}
Ensure the JWT response is evaluated on the backend. This process must include validating the token's signature and verifying the `isAlive` and `isMatch` fields. Do not perform these validations on the client side.
{% endhint %}

### Using `cause` and `description`

The `cause` parameter returns a stable constant string (for example `"TOO_DARK"` or `"FACE_TOO_FAR"`). Do not display `cause` directly to end users.

For `FaceRecognitionFailure`, prefer `description` as a fallback when you do not maintain a custom mapping. Otherwise, map `cause` to a localized, user-friendly message in your UI.

#### Available failure causes

When using the [iProov](https://github.com/iProov/android) provider, if capture succeeds but the engine fails to authenticate or process the face, the SDK returns a `FaceRecognitionFailure`.

Possible `cause` values:

|        Cause        | Description                |
| :-----------------: | -------------------------- |
|      `UNKNOWN`      | Generic failure            |
| `TOO_MUCH_MOVEMENT` | Excessive head motion      |
|     `TOO_BRIGHT`    | Over-illumination          |
|      `TOO_DARK`     | Low light conditions       |
|  `MISALIGNED_FACE`  | Face alignment failure     |
|    `FACE_TOO_FAR`   | Face too distant           |
|   `FACE_TOO_CLOSE`  | Face too close             |
|     `SUNGLASSES`    | Eye-obscuring eyewear      |
|   `OBSCURED_FACE`   | Partial face obstruction   |
|    `EYES_CLOSED`    | Closed eyes during capture |
|   `MULTIPLE_FACES`  | Multiple faces detected    |
|  `BACKGROUND_ISSUE` | Unsuitable background      |
|    `DEVICE_ISSUE`   | Incompatible device        |
|      `EYEWEAR`      | Eyewear detected           |
|   `FACE_NOT_FOUND`  | Face detection failure     |
|   `FRAMES_BLURRY`   | Blurry frames detected     |
|    `MOTION_ISSUE`   | Device motion error        |
|  `LIGHTING_ISSUES`  | Poor lighting conditions   |
|      `REJECTED`     | Transaction rejected       |
|    `SYSTEM_ERROR`   | Internal system error      |
|      `TIMEOUT`      | Session timeout            |
|   `USER_NOT_FOUND`  | User lookup failure        |
|   `DEVICE_RESTART`  | Device state error         |
|  `PROCESSING_FAULT` | Processing error           |

The previous CafSdk docs also marked which causes apply to GPA vs LA providers. Treat the table above as the full set of identifiers you may receive; map unknown values to a generic retry message.

### Example implementation

```dart
String messageForLivenessFailure(CerttaLivenessFailure failure) {
  if (failure.type == 'ImageCaptureFailure') {
    debugPrint('Capture failed before reaching the server.');
  } else if (failure.type == 'FaceRecognitionFailure') {
    debugPrint('Server rejected the capture. Response: ${failure.response}');
    if (failure.description != null && failure.description!.isNotEmpty) {
      return failure.description!;
    }
  }

  switch (failure.cause) {
    case 'TOO_DARK':
      return "It's a bit too dark. Please move to a brighter room.";
    case 'FACE_TOO_FAR':
      return 'Please bring the phone closer to your face.';
    case 'EYES_CLOSED':
      return 'Please keep your eyes open and look directly at the camera.';
    case 'MULTIPLE_FACES':
      return 'Ensure you are the only one in the frame.';
    case 'TIMEOUT':
      return 'Time ran out. Please try again when you are ready.';
    case 'REJECTED':
    case 'FACE_AUTHENTICATION':
      return "We couldn't verify your face. Please try again.";
    default:
      return 'An unexpected error occurred (${failure.cause}). Please try again.';
  }
}
```


---

# 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 by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.caf.io/caf-sdk/flutter/face-liveness/handling-failures.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
