The error envelope
Every error response has the same shape:
There is no
hint or retryable field — retryability is conveyed by HTTP status alone. Error envelopes never include the access block; the X-RateLimit-* headers are set on every response that reaches the rate limiter, including 400/404/429/5xx errors — but 401/403 authentication errors are rejected before the rate limiter runs and carry no X-RateLimit-* headers.
Error codes
These are the codes the API emits today, exhaustively:The two 404 codes are ownership-scoped: on
/v1/feedback they mean the target does not belong to your account, not necessarily that it never existed.Warnings
Warnings are the API degrading gracefully instead of failing. They share the error shape —code, message, optional details — and arrive in a warnings array on search and document responses (/v1/feedback has no warnings field):
warnings survives every verbosity preset and budget shed.
Missing passage IDs and over-restrictive source policies are deliberately warnings, not errors — your agent gets whatever is available plus a signal about what it did not get.
Retrying
Retry429 and 5xx with backoff; treat 4xx (other than 429) as bugs in the request, not transient failures.
- There is no
Retry-Afterheader. On 429, readX-RateLimit-Reset(RFC3339) — limits use fixed one-second windows, so the reset is at most about a second away. Exponential backoff also works fine. - Rate-limit tokens are spent before validation, so retry loops sending invalid bodies still burn budget.
- The SDKs and CLI retry automatically: the CLI retries 429 and 500+ up to 3 times with exponential backoff capped at 8 seconds (
--no-retrydisables).
CLI exit codes
caesar-search maps outcomes to exit codes so scripts branch on status, not output parsing. See CLI automation.
With
--json, CLI errors go to stderr as {"error":{"code","message","hint"}} — the code is taken from the API envelope’s error.code when one exists. Exit code 1 is not part of the contract.