Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 35 additions & 11 deletions content/docs/api/client-sdk.mdx
Original file line numberDiff line numberDiff line change
Expand Up@@ -666,17 +666,41 @@ field-anchored".

### Error Codes

| Code | HTTP | Category | Retryable | Description |
|:-----|:-----|:---------|:----------|:------------|
| `VALIDATION_ERROR` | 400 | validation | No | Input validation failed |
| `INVALID_QUERY` | 400 | validation | No | Malformed query expression |
| `UNAUTHENTICATED` | 401 | authentication | No | Authentication required |
| `PERMISSION_DENIED` | 403 | authorization | No | Insufficient permissions |
| `RESOURCE_NOT_FOUND` | 404 | not_found | No | Resource does not exist |
| `RATE_LIMIT_EXCEEDED` | 429 | rate_limit | Yes | Too many requests |
| `INTERNAL_ERROR` | 500 | server | Yes | Unexpected server error |
| `SERVICE_UNAVAILABLE` | 503 | server | Yes | Service temporarily unavailable |
| `NOT_IMPLEMENTED` | 501 | server | No | Service not installed (plugin missing) |
`error.code` is drawn from a **two-tier vocabulary** (ADR-0112), and
`packages/spec` is the authority for the full set:

- **Standard catalog** — the closed `StandardErrorCode` enum
(`packages/spec/src/api/errors.zod.ts`): generic conditions with
platform-wide HTTP semantics. It does not grow when a service invents a code.
- **Ledger-registered codes** — the service-specific codes each package
registers in `ERROR_CODE_LEDGER`
(`packages/spec/src/api/error-code-ledger.zod.ts`).

The exported `ErrorCode` schema is the **union** of the two, so a code being
absent from `StandardErrorCode` does not make it unofficial or unsupported:
`VALIDATION_FAILED` — the code the examples above branch on — is a
ledger-registered code, as are several others this page's examples use. The
table below is a hand-picked subset of the codes you are most likely to branch
on, **not** either tier in full; the **Tier** column says which set a row comes
from.

| Code | Tier | HTTP | Category | Retryable | Description |
|:-----|:-----|:-----|:---------|:----------|:------------|
| `VALIDATION_ERROR` | standard | 400 | validation | No | The **request** was refused before any record was validated — a repeated query parameter, a filter outside the allowlist, a malformed argument |
| `VALIDATION_FAILED` | ledger | 400 | validation | No | A **record** failed validation on a write; carries `fields[]`. This — not `VALIDATION_ERROR` — is what a per-field failure arrives as |
| `INVALID_QUERY` | standard | 400 | validation | No | Malformed query expression |
| `UNAUTHENTICATED` | standard | 401 | authentication | No | Authentication required |
| `PERMISSION_DENIED` | standard | 403 | authorization | No | Insufficient permissions |
| `RESOURCE_NOT_FOUND` | standard | 404 | not_found | No | Resource does not exist |
| `RATE_LIMIT_EXCEEDED` | standard | 429 | rate_limit | Yes | Too many requests |
| `INTERNAL_ERROR` | standard | 500 | server | Yes | Unexpected server error |
| `SERVICE_UNAVAILABLE` | standard | 503 | server | Yes | Service temporarily unavailable |
| `NOT_IMPLEMENTED` | standard | 501 | server | No | Service not installed (plugin missing) |

`Category` and `Retryable` above are the **semantic** classification of each
condition. Neither is guaranteed on the wire — as the narrowing example shows,
`error.category` and `error.retryable` are present only when the server sent
them, and the REST server's per-field validation envelope sends neither.

---

Expand Down
Loading