Skip to content

Commit 5b76c2b

Browse files
committed
docs(client-sdk): state the two-tier error-code vocabulary and add the VALIDATION_FAILED row
The `### Error Codes` table listed nine codes, all members of the closed `StandardErrorCode` enum, with no statement of what it enumerates. It omitted `VALIDATION_FAILED` — the ledger-registered code both of the page's own error-handling examples branch on — so a reader working from the table concludes a per-field validation failure arrives as `VALIDATION_ERROR` and writes a branch that never matches. Measured first, because adding the row to a table of enum members would have made the table false: the table is 9 of the enum's 50 members, so it was never "the enum" — it is a curated subset with an unstated contract. The fix is therefore the contract plus the row, not the row alone. - State the two-tier vocabulary (ADR-0112): the closed `StandardErrorCode` catalog plus the per-package `ERROR_CODE_LEDGER`, with the exported `ErrorCode` schema as their union and `packages/spec` as the authority. - Add a `Tier` column so each row says which set it comes from, and add the `VALIDATION_FAILED` row (ledger, 400). - Sharpen `VALIDATION_ERROR`'s description to the distinction that actually holds on the wire: request-shape refusals (repeated query parameter, disallowed filter, malformed argument) versus record-level validation, which answers `VALIDATION_FAILED` and carries `fields[]`. - Note that `Category`/`Retryable` are semantic classification, not wire guarantees — `packages/rest` sets neither on the per-field envelope. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UjM2ia8Av1v5NqfqQEQmC6
1 parent 68c5dba commit 5b76c2b

1 file changed

Lines changed: 35 additions & 11 deletions

File tree

content/docs/api/client-sdk.mdx

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -666,17 +666,41 @@ field-anchored".
666666

667667
### Error Codes
668668

669-
| Code | HTTP | Category | Retryable | Description |
670-
|:-----|:-----|:---------|:----------|:------------|
671-
| `VALIDATION_ERROR` | 400 | validation | No | Input validation failed |
672-
| `INVALID_QUERY` | 400 | validation | No | Malformed query expression |
673-
| `UNAUTHENTICATED` | 401 | authentication | No | Authentication required |
674-
| `PERMISSION_DENIED` | 403 | authorization | No | Insufficient permissions |
675-
| `RESOURCE_NOT_FOUND` | 404 | not_found | No | Resource does not exist |
676-
| `RATE_LIMIT_EXCEEDED` | 429 | rate_limit | Yes | Too many requests |
677-
| `INTERNAL_ERROR` | 500 | server | Yes | Unexpected server error |
678-
| `SERVICE_UNAVAILABLE` | 503 | server | Yes | Service temporarily unavailable |
679-
| `NOT_IMPLEMENTED` | 501 | server | No | Service not installed (plugin missing) |
669+
`error.code` is drawn from a **two-tier vocabulary** (ADR-0112), and
670+
`packages/spec` is the authority for the full set:
671+
672+
- **Standard catalog** — the closed `StandardErrorCode` enum
673+
(`packages/spec/src/api/errors.zod.ts`): generic conditions with
674+
platform-wide HTTP semantics. It does not grow when a service invents a code.
675+
- **Ledger-registered codes** — the service-specific codes each package
676+
registers in `ERROR_CODE_LEDGER`
677+
(`packages/spec/src/api/error-code-ledger.zod.ts`).
678+
679+
The exported `ErrorCode` schema is the **union** of the two, so a code being
680+
absent from `StandardErrorCode` does not make it unofficial or unsupported:
681+
`VALIDATION_FAILED` — the code the examples above branch on — is a
682+
ledger-registered code, as are several others this page's examples use. The
683+
table below is a hand-picked subset of the codes you are most likely to branch
684+
on, **not** either tier in full; the **Tier** column says which set a row comes
685+
from.
686+
687+
| Code | Tier | HTTP | Category | Retryable | Description |
688+
|:-----|:-----|:-----|:---------|:----------|:------------|
689+
| `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 |
690+
| `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 |
691+
| `INVALID_QUERY` | standard | 400 | validation | No | Malformed query expression |
692+
| `UNAUTHENTICATED` | standard | 401 | authentication | No | Authentication required |
693+
| `PERMISSION_DENIED` | standard | 403 | authorization | No | Insufficient permissions |
694+
| `RESOURCE_NOT_FOUND` | standard | 404 | not_found | No | Resource does not exist |
695+
| `RATE_LIMIT_EXCEEDED` | standard | 429 | rate_limit | Yes | Too many requests |
696+
| `INTERNAL_ERROR` | standard | 500 | server | Yes | Unexpected server error |
697+
| `SERVICE_UNAVAILABLE` | standard | 503 | server | Yes | Service temporarily unavailable |
698+
| `NOT_IMPLEMENTED` | standard | 501 | server | No | Service not installed (plugin missing) |
699+
700+
`Category` and `Retryable` above are the **semantic** classification of each
701+
condition. Neither is guaranteed on the wire — as the narrowing example shows,
702+
`error.category` and `error.retryable` are present only when the server sent
703+
them, and the REST server's per-field validation envelope sends neither.
680704

681705
---
682706

0 commit comments

Comments
 (0)