# Errors
URL: /docs/lan/errors

Error replies, which results are definite, recovery after a dropped connection, and concurrency.

An error reply has a code and a message and nothing else:

```json
{ "error_code": "PP_A2A_012", "error_message": "İşlem sırasında hata oluştu: Kart reddedildi" }
```

Detect errors by the **presence of `error_code`**. `error_message` is always
Turkish and can be shown to the cashier; branch your logic on the code.

## Error codes

| Code         | Meaning                                                                       | Result       |
| ------------ | ----------------------------------------------------------------------------- | ------------ |
| `PP_A2A_001` | The request is empty.                                                         | Not started  |
| `PP_A2A_002` | `data` is missing or is not an object.                                        | Not started  |
| `PP_A2A_003` | `api_key` / `secret_key` missing or invalid.                                  | Not started  |
| `PP_A2A_004` | Invalid `EOD`: `is_all` is false and `types` is empty.                        | Not started  |
| `PP_A2A_005` | Invalid `PARAMETERS`: `is_all` is false and `types` is empty.                 | Not started  |
| `PP_A2A_006` | Invalid request parameter.                                                    | Not started  |
| `PP_A2A_007` | Unexpected error while handling the request.                                  | **Unknown**  |
| `PP_A2A_008` | `header` is missing.                                                          | Not started  |
| `PP_A2A_009` | `transaction_type` is missing or unknown.                                     | Not started  |
| `PP_A2A_010` | A header field this command needs is missing.                                 | Not started  |
| `PP_A2A_011` | Nobody is logged in to POS+ on the device.                                    | Not started  |
| `PP_A2A_012` | The operation failed: declined, cancelled by the user, order closed …         | **Definite** |
| `PP_A2A_015` | The device is busy with another request.                                      | Not started  |
| `PP_A2A_016` | `TRANSACTION_RESULT`: no transaction with that id.                            | Definite     |
| `PP_A2A_017` | Authentication could not be performed (device offline or server unreachable). | Not started  |

## Recovery

The device completes an operation even when your connection drops. If you send
a payment and do not get a reply — a socket error, a timeout, your software
restarted — the customer may already have paid.

<Callout type="error">
  Never send the payment again just because the reply did not arrive. Query
  first. Charging twice is the most expensive integration bug.
</Callout>

1. Before sending a payment, persist the `order_code` as *result unknown*.
2. If the reply arrives, record it and clear the flag.
3. Otherwise open a new connection and send
   [`ORDER_RESULT`](/docs/lan/commands#order_result) with that `order_code`
   (or [`TRANSACTION_RESULT`](/docs/lan/commands#transaction_result) if you
   have the transaction id).
4. `amount_due: 0` → paid. Otherwise nothing was collected and you may start a
   new payment.
5. Do the same at start-up for every order still flagged.

The same applies to `PP_A2A_007`.

## Concurrency

A POS+ device runs **one operation at a time**, across all clients — another
till, another connection or an App2App app on the device itself. A request that
arrives while one is running is rejected at once with `PP_A2A_015`.

* Do not send requests in parallel, not even on separate connections.
* On `PP_A2A_015`, wait and retry; show the cashier that the device is busy.
* Within one connection, requests are handled in order, so a queue on your side
  is only needed when several parts of your software share a device.