# Errors

> The error envelope every failed request returns, and the stable error codes you can expect.

Canonical page: https://www.rundiffusion.com/docs/api/errors

---

Every non-2xx response uses the same envelope, including validation failures and
rate limiting. There is no second error format to handle.

```json
{
  "error": {
    "code": "INVALID_CURSOR",
    "message": "Cursor is not valid base64-encoded JSON.",
    "retryable": false,
    "details": {
      "param": "cursor"
    }
  }
}
```

`details` carries whatever structured context that specific code has to offer,
and it is omitted entirely when there is none. Read it per code rather than
expecting a fixed shape.

| Name | Type | Description |
| --- | --- | --- |
| `code` | string | A stable machine-readable identifier. Branch on this, never on the message. |
| `message` | string | Human-readable explanation. Wording may change at any time, so do not parse it. |
| `retryable` | boolean | Whether retrying the identical request could plausibly succeed. When false, something must change first. |
| `details` | object | Additional context for the specific error, such as which field failed validation. |

> **Branch on code, not on status**
>
> HTTP status tells you the category. `code` tells you what actually happened.
> A `400` covering six distinct conditions is only actionable through the code.

## Retry behavior

Treat `retryable` as the signal for whether to try again.

- **`retryable: true`** is safe to retry with exponential backoff. Add jitter so
  a fleet of clients does not synchronize.
- **`retryable: false`** will keep failing until something changes. Surface it
  rather than looping.

## Request and validation

| Code | Meaning |
| --- | --- |
| `INVALID_REQUEST` | The body or query failed validation. `details` names the field. |
| `INVALID_CURSOR` | The pagination cursor is malformed or expired. Restart from the first page. |

## Authentication and identity

| Code | Meaning |
| --- | --- |
| `TOKEN_INVALID` | The bearer token is not recognized. |
| `TOKEN_EXPIRED` | The token has expired. Refresh it or run the device flow again. |
| `TOKEN_REVOKED` | The token was revoked, by regenerating or deleting an access token, or by signing out everywhere. |
| `ANONYMOUS_NOT_ALLOWED` | This endpoint needs an authenticated caller. |
| `EMAIL_NOT_VERIFIED` | The account's email is not yet verified. |
| `ACCOUNT_SELECTION_INVALID` | The `team_id` selection is malformed: an empty value, a list with blank entries, too many teams, or several IDs where the endpoint acts on one team. |
| `ACCOUNT_HEADER_INVALID` | The selection is not one this credential may act on, such as a company token asked to act personal. |
| `ACCOUNT_NOT_MEMBER` | The caller is not a member of that account. |
| `ACCOUNT_NOT_FOUND` | No such account. |

## Permissions and quota

| Code | Meaning |
| --- | --- |
| `PERMISSION_DENIED` | The account's role does not permit this action. |
| `PLAN_LIMIT_REACHED` | The account's plan caps something it has reached, such as the number of nodes on a board. The fix is a plan change, not a role change. `details` carries `limit` and `current`. |
| `PLUGIN_NOT_ALLOWED` | The active team's role does not permit this way of reaching the API. See [OAuth device flow](/docs/api/authentication#oauth-device-flow). |
| `DOMAIN_LOCKED` | The caller's email domain is claimed by a company and the active account is not that company. |
| `INSUFFICIENT_BALANCE` | The account does not have enough tokens. |
| `TOKEN_LIMIT` | The account hit its configured token ceiling. |
| `TOKEN_COOLDOWN` | The account is in a spending cooldown. |

## Not found

| Code | Meaning |
| --- | --- |
| `TOOL_NOT_FOUND` | No tool with that id, or it is not visible to this caller. |
| `TOOL_TAG_NOT_FOUND` | No tool tag with that id. |
| `BOARD_NOT_FOUND` | No board with that id, or it is not visible to this caller. |
| `NODE_NOT_FOUND` | No node with that id on that board. |
| `LIBRARY_ITEM_NOT_FOUND` | No generation with that id, or it is not visible to this caller. |
| `UPLOAD_NOT_FOUND` | No upload with that id, or it has been deleted. |
| `REQUEST_NOT_FOUND` | No run with that `request_id`. |
| `LIBRARY_REF_NOT_FOUND` | A `LIBRARY_REF` in `inputs` points at a generation that does not exist. `details` carries the `field_key`. |

A missing resource and a resource you cannot see return the same code, so
absence never confirms that something exists.

## Generation

| Code | Meaning |
| --- | --- |
| `TOOL_SCHEMA_STALE` | The tool changed after you fetched it. `details.expected_tool_fields_hash` carries the live value. Re-fetch [the tool](/docs/api/tools/get) and rebuild. |
| `NUM_RESULTS_LIMIT` | `num_results` exceeds what the tool or the account's role permits. |
| `ACTIVE_RUN_CAP` | Too many runs already in flight for this identity. Wait for one to finish. |
| `IDEMPOTENCY_KEY_CONFLICT` | The `Idempotency-Key` was reused with a different body. Use a fresh key. |
| `CONTENT_POLICY_VIOLATION` | The request was blocked. Do not retry it unchanged. |
| `NSFW` | A result was withheld for content reasons. The run itself completed. |
| `PROVIDER_FAILURE` | The upstream model provider failed. Retryable. |
| `TOOL_NOT_RUNNABLE_V1` | The tool needs a `MODEL` input, which this API cannot supply yet. Pick a different tool. |

## Inputs and uploads

| Code | Meaning |
| --- | --- |
| `TOO_MANY_INPUT_FILES` | More than 12 files across multipart parts and `UPLOAD_REF` / `LIBRARY_REF` references combined. |
| `UPLOAD_TOO_LARGE` | A file exceeds the 50 MB per-file limit. |
| `UNSUPPORTED_MEDIA_TYPE` | The file's type is not accepted. |
| `METADATA_TOO_LARGE` | `metadata` exceeds 8 KB serialized. |
| `INVALID_WEBHOOK_URL` | `webhook_url` is not a public HTTPS URL. See [Webhooks](/docs/api/generate/webhooks). |

## Concurrent edits

| Code | Meaning |
| --- | --- |
| `BOARD_CHANGED` | The board was modified between your read and your write, so the order you sent no longer describes it. Re-read the board and reapply. |

## Device code flow

| Code | Meaning |
| --- | --- |
| `AUTHORIZATION_PENDING` | Expected. The user has not finished approving yet. Keep polling. |
| `SLOW_DOWN` | Expected. You are polling too fast. Increase your interval. |
| `DEVICE_CODE_NOT_FOUND` | The device code is unknown. Restart the flow. |
| `DEVICE_CODE_EXPIRED` | The device code expired before approval. Restart the flow. |

> `AUTHORIZATION_PENDING` and `SLOW_DOWN` are normal parts of the device flow.
> Treat every other code in this section as terminal.

## Infrastructure

| Code | Meaning |
| --- | --- |
| `INTERNAL_ERROR` | Something failed on our side. Retryable with backoff. |
| `UPSTREAM_UNAVAILABLE` | A dependency was unavailable. Retryable with backoff. |
| `RATE_LIMITED` | You exceeded a rate limit. See [Rate limits](/docs/api/rate-limits). |

If one of the first two keeps coming back for the same request, backoff is not
going to fix it. Send the endpoint, the code, and roughly when it happened to
[our support team](https://www.rundiffusion.com/contact).
