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

# Errors

> One error shape, one code per reason, and whether money moved.

Buyer API refusals use the error shape below. The shared source-list route
can return the Gateway v2 envelope with an <code>errors</code> array for
a request without a key or with an invalid query. Failed runs return the
run object with an embedded <code>error</code> and <code>usage</code>, so
you can see what was charged. Read the run errors and usage together; a
provider failure does not by itself mean the whole run was free.

```json theme={null}
{
  "error": {
    "code": "insufficient_balance",
    "message": "The balance is too low for this plan's maximum cost. Top up in Apiosk > Balance. Nothing was charged.",
    "required": { "amount": 0.42, "amount_micro_usd": "420000", "currency": "USD" },
    "available": { "amount": 0.17, "amount_micro_usd": "170000", "currency": "USD" }
  }
}
```

`code` is stable and safe to branch on. `message` is written for a person
reading a log. Extra fields depend on the code and are listed below.

## Buyer API and shared directory codes

| Status | Code                              | Extra fields                             | Meaning                                                                    |
| ------ | --------------------------------- | ---------------------------------------- | -------------------------------------------------------------------------- |
| 400    | `invalid_request`                 | —                                        | Malformed body, `max_spend`, `limit` or `Idempotency-Key`                  |
| 400    | <code>invalid\_query</code>       | —                                        | Invalid directory or endpoint-index filter                                 |
| 400    | `invalid_workspace`               | —                                        | Organisation keys do not take a workspace header                           |
| 400    | `invalid_continuation_state`      | —                                        | Copy the signed continuation state unchanged from the run                  |
| 401    | `invalid_api_key`                 | —                                        | Missing or unknown key                                                     |
| 401    | `api_key_expired`                 | —                                        | Past its expiry date; create a new key                                     |
| 401    | `api_key_revoked`                 | —                                        | Revoked in the app                                                         |
| 402    | `insufficient_balance`            | `required`, `available`                  | Top up the organisation's balance                                          |
| 402    | `spend_limit_exceeded`            | `limit`, `required`, `available`         | `limit` is `per_request`, `monthly` or `max_spend`                         |
| 403    | `insufficient_scope`              | `required_scope`                         | The key lacks that permission                                              |
| 403    | `organisation_unavailable`        | —                                        | The organisation cannot spend from its main balance right now              |
| 403    | `spending_not_permitted`          | —                                        | This caller may not spend in this workspace                                |
| 404    | `source_not_found`                | —                                        | No purchasable source with this id or name                                 |
| 404    | `operation_not_found`             | `operations`                             | The source has no such operation                                           |
| 404    | `run_not_found`                   | —                                        | No run with this id belongs to this key                                    |
| 409    | `idempotency_conflict`            | —                                        | Same `Idempotency-Key`, different body                                     |
| 409    | `ambiguous_operation`             | `sources`                                | Two sources in a group offer this operation; call one by id                |
| 409    | `request_in_progress`             | `retry_after_ms`                         | The first request with this key is still running                           |
| 409    | `plan_not_ready`                  | —                                        | The ask has no priced plan yet                                             |
| 409    | `plan_expired`                    | —                                        | The plan's price expired; ask again                                        |
| 409    | `plan_changed`                    | —                                        | The plan changed while it was being approved                               |
| 409    | `continuation_changed`            | —                                        | Read the run and review its current continuation; do not repeat a paid Ask |
| 409    | `run_cancelled`                   | —                                        | The run was cancelled and is not restarted                                 |
| 422    | `invalid_source_input`            | `field`, `expected`, `missing`, `schema` | An input is unknown, missing or malformed                                  |
| 422    | `invalid_continuation_input`      | —                                        | Use an offered candidate or a value matching the action schema             |
| 422    | `unsupported`                     | —                                        | No source can answer this question                                         |
| 429    | <code>busy</code>                 | —                                        | Shared directory concurrency limit; retry shortly                          |
| 429    | `rate_limit_exceeded`             | —                                        | Too many requests; wait and retry with the same key                        |
| 502    | `source_error`                    | —                                        | The source could not complete the run                                      |
| 503    | <code>planner\_unavailable</code> | —                                        | The question could not be interpreted right now                            |
| 503    | <code>catalog\_unavailable</code> | —                                        | Source directory unavailable; retry shortly                                |
| 503    | `source_unavailable`              | —                                        | The source is temporarily unreachable                                      |
| 503    | `service_unavailable`             | —                                        | Apiosk could not complete the request; retry with the same key             |
| 503    | `approval_unconfirmed`            | —                                        | Read the run's status before retrying; do not start a new request          |
| 504    | `source_timeout`                  | —                                        | The source did not answer in time                                          |

## Did money move?

* **Authorization, validation and budget refusals before dispatch** do
  not start a purchase. A failed run can arrive after a source was called;
  inspect its usage.
* **A failed run** returns HTTP 502 with the run body: <code>usage</code>
  shows what was charged, and anything undelivered was refunded or is held
  until reconciled.
* **When in doubt, read the run.** <code>GET /v2/runs/{id}</code> is free
  and authoritative.

## Retrying

| Code                                                                                 | Retry?                                                                                 |
| ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- |
| `rate_limit_exceeded`, `service_unavailable`, `source_unavailable`, `source_timeout` | Yes, with the same `Idempotency-Key`                                                   |
| `request_in_progress`                                                                | Yes, after `retry_after_ms`                                                            |
| `approval_unconfirmed`                                                               | Read the run first; do not start a new request                                         |
| `continuation_changed`                                                               | Read the run, then use its current action/state and a new key if input is still needed |
| `insufficient_balance`, `spend_limit_exceeded`                                       | Only after topping up or raising the limit                                             |
| `invalid_source_input`, `insufficient_scope`, `idempotency_conflict`                 | No; fix the request                                                                    |
