Concepts
How does error handling work?
44 stable error codes with structured detail, imperative remediation, and typed recovery actions.
Design principles
- Codes are stable. Never renamed — agents branch on them.
- Remediation is imperative. "Call
POST /v1/quotesagain" — not "An error occurred." - Detail is structured. IDs, states, amounts as typed fields.
- next_actions is a closed vocabulary. Adding a new
relis a contract change. - Errors on idempotent paths are sealed. Replays return the cached failure.
Error categories
| Status | Category | Count | Agent strategy |
|---|---|---|---|
| 400 | Validation | 1 | Fix detail.field_errors, retry |
| 401 | Auth / delegation | 4 | Reattach credentials or remint JWT |
| 402 | Payment | 1 | Retry with new token or surface provider message |
| 403 | Authorization | 3 | Request elevated scope |
| 404 | Not found | 11 | Discover valid IDs via search/list |
| 409 | Conflict | 14 | Read detail, follow next_actions |
| 410 | Gone (expired) | 2 | Requote and retry |
| 429 | Rate limit | 1 | Back off for retry_after seconds |
| 500 | Internal | 2 | Retry with backoff, cite trace_id |
| 502 | Upstream | 2 | Retry with backoff |
Error recovery decision tree
error.code?
├── *_not_found (404) → discover valid IDs via search/list
├── *_expired (410) → requote, resource TTL elapsed
├── allocation_failed (409) → inventory moved, search alternatives
├── payment_failed (402) → new payment token or surface provider_message
├── rate_limited (429) → sleep(retry_after), retry
├── delegation_* (401) → mint/remint JWT, or escalate to admin
├── idempotency_conflict (409) → replay original body or use new key
├── validation_failed (400) → fix detail.field_errors, retry
└── internal_error (500) → retry with backoff, cite trace_idSee the full Error Catalog for all 44 error codes.