# Error handling
URL: /docs/a2a/error-handling

Catch and interpret A2A errors and error codes.

Every operation either resolves with a typed response or fails with a typed
error. On Flutter and React Native the error type is `PPA2AException`; on the
native package it is `PlusPayException`. All of them expose an **error code**
and a **message**.

|         | Flutter          | React Native     | Native               |
| ------- | ---------------- | ---------------- | -------------------- |
| Type    | `PPA2AException` | `PPA2AException` | `PlusPayException`   |
| Code    | `errorCode`      | `errorCode`      | `getError_code()`    |
| Message | `message`        | `message`        | `getError_message()` |

## Catching errors

<CodeBlockTabs defaultValue="flutter" groupId="pp-platform" persist>
  <CodeBlockTabsList>
    <CodeBlockTabsTrigger value="flutter">
      Flutter
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="kotlin">
      Kotlin
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="java">
      Java
    </CodeBlockTabsTrigger>

    <CodeBlockTabsTrigger value="react-native">
      React Native
    </CodeBlockTabsTrigger>
  </CodeBlockTabsList>

  <CodeBlockTab value="flutter">
    ```dart
    try {
      final result = await _pluspay.startPayment(request);
      // success
    } on PPA2AException catch (e) {
      print(e.errorCode); // e.g. "LAUNCH_INTENT_ERROR"
      print(e.message);   // human-readable message
    }
    ```
  </CodeBlockTab>

  <CodeBlockTab value="kotlin">
    ```kotlin
    // Coroutine style
    try {
        val result = client.startPayment(request)
    } catch (e: PlusPayException) {
        Log.e("A2A", "${e.error_code}: ${e.error_message}")
    }

    // Callback style
    client.startPayment(request, object : PPA2ACallback<PPStartPaymentResponseModel> {
        override fun onSuccess(result: PPStartPaymentResponseModel) {}
        override fun onError(e: PlusPayException) {
            Log.e("A2A", "${e.error_code}: ${e.error_message}")
        }
    })
    ```
  </CodeBlockTab>

  <CodeBlockTab value="java">
    ```java
    client.startPayment(request, new PPA2ACallback<PPStartPaymentResponseModel>() {
        @Override
        public void onSuccess(PPStartPaymentResponseModel result) { }

        @Override
        public void onError(PlusPayException e) {
            Log.e("A2A", e.getError_code() + ": " + e.getError_message());
        }
    });
    ```
  </CodeBlockTab>

  <CodeBlockTab value="react-native">
    ```ts
    try {
      const result = await client.startPayment(request);
      // success
    } catch (e) {
      if (e instanceof PPA2AException) {
        console.log(e.errorCode); // e.g. "LAUNCH_INTENT_ERROR"
        console.log(e.message);   // human-readable message
      }
    }
    ```
  </CodeBlockTab>
</CodeBlockTabs>

## Error codes

Branch on `errorCode`, never on the message text. Flutter (0.8.0+) and React
Native (0.4.0+) also expose these codes as constants in `PPA2AErrorCodes`.

### Returned by the client package

| Code                  | Meaning                                                                                                                                                           |
| --------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `LAUNCH_INTENT_ERROR` | POS+ could not be launched (not installed, or the intent could not be resolved).                                                                                  |
| `PP-A2A-PARSE`        | The response from POS+ could not be parsed.                                                                                                                       |
| `PP_A2A_015`          | Another request from your app is still waiting for its result. Flutter 0.8.0+ and React Native 0.4.0+ reject the new request immediately, without launching POS+. |
| `NOT_INITIALIZED`     | React Native: `initialize()` was not called before the request.                                                                                                   |
| `TIMEOUT`             | Native package: no response from POS+ within the client timeout.                                                                                                  |

### Returned by POS+

| Code         | Meaning                                                                                                                                                               |
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PP-A2A-003` | The intent did not contain a `request` extra. Note the hyphens: this is not `PP_A2A_003`.                                                                             |
| `PP_A2A_001` | The request is empty.                                                                                                                                                 |
| `PP_A2A_002` | This operation needs a `data` object, but it is missing or is not an object.                                                                                          |
| `PP_A2A_003` | The `client_token` in the header is invalid (only checked when a token is sent).                                                                                      |
| `PP_A2A_004` | Invalid end-of-day request: `is_all` is `false` but `types` is empty.                                                                                                 |
| `PP_A2A_005` | Invalid parameter request: `is_all` is `false` but `types` is empty.                                                                                                  |
| `PP_A2A_006` | Invalid request parameter.                                                                                                                                            |
| `PP_A2A_007` | Unexpected error while handling the request. The final state is unknown: query it with [`getTransactionResult` / `getOrderResult`](/docs/a2a/recovery).               |
| `PP_A2A_008` | The `header` is missing.                                                                                                                                              |
| `PP_A2A_009` | `transaction_type` is missing or not a known operation.                                                                                                               |
| `PP_A2A_010` | A header field required by this operation is missing or empty (for example `order_code` or `transaction_id`).                                                         |
| `PP_A2A_011` | Nobody has ever logged in to POS+ on this device. Log in to POS+ once, then retry.                                                                                    |
| `PP_A2A_012` | The operation failed in POS+ (declined, cancelled by the user, order closed, …). The message describes the cause.                                                     |
| `PP_A2A_013` | POS+ could not log in automatically. Check the device's internet connection and the POS+ session.                                                                     |
| `PP_A2A_014` | POS+ timed out: it could not get ready within 3 minutes, or the request did not finish within 15 minutes. The final state is unknown: [query it](/docs/a2a/recovery). |
| `PP_A2A_015` | POS+ is still processing an earlier request. Wait for that result, then retry.                                                                                        |
| `PP_A2A_016` | `TRANSACTION_RESULT`: the order has no transaction with the given `transaction_id`.                                                                                   |

<Callout type="warn">
  `error_message` is always in **Turkish**, even when you set a
  [`locale`](/docs/a2a/initialization#language). For example: *"İşlem kullanıcı
  veya sistem tarafından iptal edildi!"* ("The transaction was cancelled by the
  user or the system."). If you show errors to your users in another language,
  map `errorCode` to your own text.
</Callout>

## Timeouts and concurrency

* **One request at a time.** POS+ handles a single A2A request at a time. A
  second request that arrives while one is in progress is rejected with
  `PP_A2A_015`. Wait for the first result before you send the next request.
* **POS+ start-up: 3 minutes.** If POS+ is launched by your request and cannot
  get ready to handle it within 3 minutes, it answers with `PP_A2A_014`.
* **Request handling: 15 minutes.** If a request is not finished within 15
  minutes, POS+ answers with `PP_A2A_014`. The limit is long on purpose so that
  a real payment flow (card reading, PIN entry, receipt) is never cut off.
* **Client-side timeouts.** The Flutter and React Native packages don't time
  out on their own. If you add your own timeout, make it **longer than 15
  minutes**. Otherwise a payment that completes in POS+ looks failed in your
  app.
* After it sends the result, POS+ brings your app back to the foreground and
  moves itself to the background.

## Recommended handling

* Treat any thrown error as a non-completed transaction. Do **not** assume the
  payment succeeded.
* For `PP_A2A_007` and `PP_A2A_014`, the outcome is unknown. Query it with
  [`getTransactionResult` / `getOrderResult`](/docs/a2a/recovery) before you
  retry.
* For `LAUNCH_INTENT_ERROR`, verify POS+ is installed and (Flutter) consider
  providing `fallbackPackageName` / `fallbackActivityName` during
  [initialization](/docs/a2a/initialization).
* For `PP_A2A_011` and `PP_A2A_013`, ask the user to open POS+ and log in.