# Errors

Every error answers with the same shape ([API › Errors](/docs/api#errors)). Branch on `code`; follow
`next_step.message`; use `next_step.url`, `next_step.api` and `next_step.cli` when they are there. An error never
holds on to an `Idempotency-Key`: after fixing the cause, retry the same request with the same key.

| Code                                              | HTTP | In short                                             |
| ------------------------------------------------- | ---- | ---------------------------------------------------- |
| [`invalid_request`](#invalid_request)             | 400  | the request is malformed or a value is wrong         |
| [`authorization_pending`](#authorization_pending) | 400  | a CLI sign-in nobody has approved yet                |
| [`access_denied`](#access_denied)                 | 400  | the person refused a CLI sign-in                     |
| [`expired_token`](#expired_token)                 | 400  | a CLI sign-in's code expired or was used             |
| [`unauthorized`](#unauthorized)                   | 401  | no key, or a key we don't know                       |
| [`insufficient_funds`](#insufficient_funds)       | 402  | the wallet or a cap cannot pay for this              |
| [`forbidden`](#forbidden)                         | 403  | the key may not do this                              |
| [`not_found`](#not_found)                         | 404  | no such thing in this workspace, or no such endpoint |
| [`idempotency_conflict`](#idempotency_conflict)   | 409  | an `Idempotency-Key` was reused for another request  |
| [`rate_limited`](#rate_limited)                   | 429  | too many requests; wait `Retry-After`                |
| [`internal_error`](#internal_error)               | 500  | something failed on our side                         |
| [`capacity_full`](#capacity_full)                 | 503  | cells are full and you asked not to queue            |
| [`capacity_unavailable`](#capacity_unavailable)   | 503  | no cell serves this class and residency at all       |

## `invalid_request`

**400.** The body is not valid JSON, a field is missing or out of range (a class that does not exist, a size the class
does not sell, a `limit` too large), `Raw-Version` names a version that does not exist, `Idempotency-Key` is not 1
to 255 characters, `Raw-Workspace` is not a workspace id, or a personal token names no workspace for a request that
acts in one. `message` names the field and what it should be. Fix the request; sending it unchanged gets the same
answer.

## `authorization_pending`

**400.** The answer to polling `POST /v1/device/token` while nobody has approved the sign-in yet (RFC 8628). Keep
polling, no faster than every `interval` seconds of the sign-in, until it is approved, refused or expired. The
[CLI](/docs/cli#sign-in) does this for you.

## `access_denied`

**400.** The person refused this sign-in on the console's page. The code is spent; start a new sign-in
(`raw login`) if it was refused by mistake.

## `expired_token`

**400.** The device code is unknown, past its ten minutes, or was used already (a sign-in gives its token once). Start a
new sign-in: `POST /v1/device/code`, or `raw login`.

## `unauthorized`

**401.** The `Authorization` header is missing or malformed, or holds a key or token that does not exist or was
revoked. Send `Authorization: Bearer` with a live API key (`rc_live_…`; `next_step.url` is the console's **API keys**
page) or the CLI's personal token (`rc_pat_…`; `next_step.cli` is `raw login`).

## `insufficient_funds`

**402.** The org's wallet, or a budget cap, cannot pay for a new sandbox: the workspace's budget says
`can_start: false`. `next_step.url` is the console's billing page and `next_step.api` the budget
(`GET /v1/workspaces/{id}/budget`). Top up or raise the cap, then retry the same request with the same
`Idempotency-Key`.

## `forbidden`

**403.** The key is valid, but this needs more than it holds: a `viewer` key cannot create sandboxes or keys, and the
wallet answers only keys of an org owner or billing member (any key can read its workspace's budget instead). A
workspace's people, invitations, name and residency are its admins': a workspace key is never an admin, so it is told
to sign in to the [CLI](/docs/cli) as one (`next_step.cli` is `raw login`), and a personal token of a member below
admin to ask one (`raw members list`). Keys act only in their own workspace (naming another with `Raw-Workspace` is
refused), personal tokens only where their person holds a relation, and neither ever at org level: an org's owners,
its name and its money are changed in the console, which `next_step.url` names ([API › Only in the
console](/docs/api#only-in-the-console)). Use a key with the relation this needs, made by a person who holds it.

## `not_found`

**404.** Nothing with that id exists in the workspace the request acts in, or no endpoint answers that method and path (then
`next_step.url` is the OpenAPI document). Ids are typed (`ws_`, `key_`, `sbx_`, …); check that the id is complete and
belongs to the workspace the request acts in.

## `idempotency_conflict`

**409.** The `Idempotency-Key` was used in the last 24 hours, by the same API key, for a different request (another
method, path or body). Send the original request to get its answer, or use a new key for new work. The same code
answers a request whose key is still being answered by an earlier one; retry it in a moment.

## `rate_limited`

**429.** Too many requests in a short time: more than an API key may send to `/v1`, or more sign-in, sign-up,
password-reset, verification or CLI sign-in attempts than one address may make ([Limits › Rate limits](/docs/limits#rate-limits)).
`Retry-After` gives the seconds to wait and `retry_at` the time (RFC 3339), so nothing parses `message`. Retry the same
request then, with the same `Idempotency-Key`.

## `internal_error`

**500.** Something failed on our side, not in your request. Nothing was kept under the `Idempotency-Key`, so retry the
same request with the same key; if it keeps failing, tell us.

## `capacity_full`

**503.** Cells that serve the class and residency exist, but they are full, and the request said `"on_full": "fail"`.
`Retry-After` gives the seconds until room is expected and `retry_at` the time (RFC 3339), so nothing parses `message`. Retry then, or leave out
`on_full` to wait in the queue instead ([Concepts › The queue](/docs/concepts#the-queue)).

## `capacity_unavailable`

**503.** No cell serves the requested class in the workspace's residency, so the request cannot be queued: nothing
would ever drain it. There is no `Retry-After`. This is the answer to every sandbox create during early access, whatever
`on_full` says; `next_step.url` is the [early-access note](/docs/limits#early-access).
