---
title: Errors
description: One error shape for every endpoint, and what to do with each status.
url: https://anyrow.ai/docs/errors
canonical_url: https://anyrow.ai/docs/errors
last_updated: '2026-10-11'
---

# Errors

A failed request returns a non-2xx status and a JSON body with the same shape everywhere:

```json
{
  "success": false,
  "status": 400,
  "status_key": "bad_request",
  "error_key": "invalid_input",
  "message": "Name is required",
  "fields": {
    "name": [{ "path": "name", "error_key": "required", "message": "Name is required" }]
  },
  "vars": {}
}
```

| Field | What it holds |
| :-- | :-- |
| `status` | The HTTP status, repeated in the body |
| `error_key` | A stable machine code: branch on this, never on `message` |
| `message` | A sentence for people; it can change |
| `fields` | For invalid input, each failing field's errors, keyed by its path |
| `vars` | Values the error refers to, such as the plan whose limit you reached |

## What to do with each status

| Status | Retry? | What to do |
| --: | :-- | :-- |
| 400, 422 | No | Fix the input; `fields` says which part is wrong |
| 401 | No | Send a valid key: see [Authentication](/docs/authentication) |
| 402 | No | The plan's limit is reached, or the feature isn't on the plan; `vars` names it |
| 403 | No | The key can't reach this project or action |
| 404 | No | The id is wrong, or the resource was deleted |
| 409 | Re-read, then retry | Someone changed the resource first (`optimistic_lock_conflict`) or it already exists |
| 413, 415 | No | The file is too large, or its type isn't supported |
| 429 | Yes, after a wait | See [Rate limits](/docs/rate-limits) |
| 500, 502, 503, 504 | Yes, with backoff | A failure on our side; retry with exponential backoff |

Each endpoint's page lists the error codes it can return, and
[the errors every endpoint shares](/docs/api/errors) are listed once.

```ts title="Node.js"
const { data, error, status } = await anyrow.table.get({
  params: { project_id: projectId, table_id: tableId },
})
if (error) {
  if (status === 404) return null
  throw new Error(`Anyrow ${status}: ${error.error_key}`)
}
```

```python title="Python"
response = requests.get(url, headers=headers)
if not response.ok:
    error = response.json()
    if response.status_code == 404:
        return None
    raise RuntimeError(f"Anyrow {error['status']}: {error['error_key']}")
```
