# Errors and retries

> The error body, the codes you will branch on, and which ones to retry.

Source: https://getchord.xyz/docs/concepts/errors/

Every non-2xx response is JSON with a stable machine code and a human message:

```json
{ "error": "INSUFFICIENT_LIQUIDITY", "message": "no route at this size — try a smaller amount" }
```

Branch on `error`, never on `message`. Some older routes add `statusCode`; ignore it.

## Codes

| Status | `error` | Where | Meaning and what to do |
| --- | --- | --- | --- |
| 400 | `INVALID_PARAMS` | everywhere | A parameter is missing or malformed. The message names it. |
| 400 | `BAD_ADDRESS` · `BAD_LIMIT` · `BAD_HOURS` · `BAD_DAYS` · `BAD_SORT` · `BAD_QUERY` · `BAD_POOL` | reads | The named check failed. |
| 400 | `UNSUPPORTED_CHAIN` · `BAD_CHAIN` | quote, swap, boards | The chain id is not one the API serves. |
| 400 | `BAD_SOURCE` | smart money | `source=onchain` is required on 5042 / 4663. |
| 400 | `SLIPPAGE_MISMATCH` | swap | The `slippageBps` you sent differs from the quote's. Re-quote at the slippage you want. |
| 400 | `ROUTE_MISMATCH` | swap | The quote body was edited. Send it back unmodified. |
| 400 | `ONLY_REQUIRES_KEY` | quote | `?only=` needs an API key. |
| 401 | `UNAUTHORIZED` | ask, me | No key or session, or an unknown one. |
| 404 | `UNKNOWN_TOKEN` | charts, trades, stats | Not on a curated list and not a side of an indexed pool. |
| 404 | `NOT_FOUND` | pool, position, report | Nothing at this identity. |
| 404 | `NO_ROUTE` | swap calldata | No route for this pair and size. |
| 404 | `NOT_ENOUGH_HISTORY` | LP plan, simulate | Fewer than 24 hourly candles or no anchored pool. |
| 409 | `ROUTE_STALE_GRADUATED` | quote | A bonding-curve token graduated mid-route (Monad). Re-quote. |
| 409 | `NOT_ATTESTED` · `IN_FLIGHT` | bridge relay | Circle has not attested the burn yet, or a relay is already running. Poll `/bridge2/status`. |
| 429 | `RATE_LIMIT_EXCEEDED` | everywhere | Read `X-RateLimit-Reset` and back off. |
| 429 | `RATE_LIMITED` | ask | Over 8 questions a minute. |
| 502 | `ENGINE_ERROR` · `ROUTE_ERROR` | quote, swap | The routing engine did not answer. Retry once after a second. |
| 503 | `NOT_READY` | arc, rh, launchers | The rollup or index has not run on this deployment yet. |
| 503 | `CHAIN_NOT_ENABLED` | quote, smart money | The chain's routing or board is not published here. |
| 503 | `HOLDERS_PENDING` | holders | The token was just pinned for tracking. Retry in a few minutes. |
| 503 | `NOT_ENABLED` | points | The points program is off on the deployment. |
| 503 | `ARC_DATA_UNAVAILABLE` · `RH_DATA_UNAVAILABLE` · `SIMULATION_UNAVAILABLE` · `IRIS_UNAVAILABLE` · `SESSION_STORE_UNAVAILABLE` · `DISABLED` | various | A dependency did not answer. Retry with backoff. |

## Empty is not an error

Some answers are honest empties, not failures:

- A quote with `routePlan: []` and `outputAmount: "0"` — no route at this size. Read `noRouteReason` (`INSUFFICIENT_LIQUIDITY` or `NO_POOLS`).
- A token on `/arc/token/{address}` that comes back as `{ address, listed: false }` — no trades in the last 24 hours.
- Portfolio routes answer an empty 200 shape (`tokens: []`) when the tape read fails, never a 5xx. Treat it as "nothing yet" and retry later.
- A holder ledger with `complete: false` — the backfill has not reached the token's creation block; balances are partial and say so.

## Retrying

Retry `502` and `503` with exponential backoff starting at one second, at most three times. Do not retry `400`, `401`, `404` or `409` — the request will not become valid on its own (a `409 NOT_ATTESTED` is a poll, not a retry). Retry `429` after `X-RateLimit-Reset`.

Quotes are live and expire: a `502` on `/swap` after a successful quote usually means the route changed. Quote again rather than resubmitting the old body.
