Skip to main content
Errors are JSON with a stable machine-readable code. Trigger one against staging right now:
curl -s https://search-api-staging-779189860552.europe-west1.run.app/v1/search \
  -H "Content-Type: application/json" \
  -d '{"query":"x","mode":"deep"}'
{
  "type": "error",
  "request_id": "7e9a1f0c-2f43-4f5a-9d3e-6b1c2a4d5e6f",
  "error": {
    "code": "unsupported_mode",
    "message": "mode must be fast, standard, or research",
    "details": { "field": "mode" }
  }
}

The error envelope

Every error response has the same shape:
FieldTypeNotes
typestringAlways the literal "error"
request_idstringUUID generated per request — quote it when reporting issues
error.codestringStable snake_case code; branch on this
error.messagestringHuman-readable; wording may change, the code will not
error.detailsobjectOptional structured context (often {"field": "..."}); omitted when empty
$schemastringOptional JSON Schema reference; present on errors returned by the API handlers, absent on middleware-emitted ones (401/403/429). Ignore it.
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:
HTTPerror.codeWhen
400validation_errorMalformed JSON, an unknown body field, a body over 1 MiB, or an invalid field value. details.field names the offender for invalid field values; malformed JSON, unknown fields, and oversized bodies return details.error with the parser message instead. Non-UUID session_id, doc_id, search_id, and passage_id values land here.
400unsupported_modemode is not fast, standard, or research.
400response_too_largeOnly when you opt in with response.budget.on_exceed: "error" and the budget cannot be met. The default (shed) degrades with warnings instead — see response shaping.
401missing_api_keyNo Bearer token on a deployment that requires keys. (Staging allows keyless access, so you will not see this there.)
401invalid_api_keyA Bearer token was sent but is malformed, unknown, expired, or revoked. A bad key never falls back to the anonymous tier.
403insufficient_scopeValid key without the endpoint’s scope (search:read, document:read, feedback:write).
404document_not_found/v1/document target not found; also /v1/feedback when the doc_id is not granted to your account.
404search_not_found/v1/feedback with a search_id that does not belong to your account.
429rate_limitedOver your tier’s requests-per-second in the current one-second window. See rate limits.
500internal_errorServer fault. Safe to retry.
502provider_unavailableThe search infrastructure is unreachable. Transient; retry.
503provider_unavailableThe deployment has no retrieval configured.
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):
{
  "code": "content_truncated",
  "message": "Returned content was truncated to content.max_chars.",
  "details": { "field": "content.text", "max_chars": 12000 }
}
Warnings never invalidate the response. A 200 with warnings is a complete answer: the status stays 200, the payload is valid and usable, and warnings survives every verbosity preset and budget shed.
codeEndpointFires when
response_truncatedsearchresponse.budget.max_chars_total forced shedding; the response’s truncated flag is set, details.shed_levels lists what was dropped.
budget_unsatisfiablesearchEven a single minimal result exceeds the budget; it is returned anyway.
unknown_fieldsearchresponse.verbosity is unrecognized; the request proceeds with standard.
source_policy_filteredsearchThe requested source_policy filtered out one or more results (details.filtered_results).
rerank_unavailablesearchReranking is temporarily unavailable; results use first-stage order and ranking.ranker_version is first_stage_order_v1.
content_unavailabledocumentDocument content could not be fetched for this request.
content_truncateddocumentContent exceeded content.max_chars; pairs with content.truncated: true and content.start_char for continuation reads.
stale_rangedocumentcontent.range.capture_id pins a capture that is no longer the latest; offsets may not line up.
stale_passage_iddocumentOne or more requested passage IDs are unavailable in the latest capture (details.passage_ids lists them).
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

Retry 429 and 5xx with backoff; treat 4xx (other than 429) as bugs in the request, not transient failures.
  • There is no Retry-After header. On 429, read X-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-retry disables).

CLI exit codes

caesar-search maps outcomes to exit codes so scripts branch on status, not output parsing. See CLI automation.
Exit codeMeaning
0Success — including a 200 with warnings
2Bad input (usage or flag errors, invalid local input)
3Auth error (HTTP 401 or 403)
4API error (any other non-2xx, after retries; also network failures)
5Timeout (default 30s; raise with --timeout)
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.