Error codes
The error body, how to tell a retriable error, and every error code the v1 API returns, with the page that explains each.
The error body
Most errors are JSON with a flat body:
{
"error_code": "INVALID_BODY",
"message": "invalid experience body at `scope`: segment 0 is missing ':' delimiter …",
"retriable": false,
"request_id": "req_01a0e828-…",
"details": { "field_path": "scope", "reason": "…", "schema": "/v1/schemas/experience_envelope" }
}error_codeis UPPERCASE with underscores, with one exception: a multipart upload to/v1/blobsanswers415 unsupported_media_typein lowercase.retriableis the rule to follow:truemeans send the identical request again (the sameidempotency_keyis safe);falsemeans change the request first. The newer409/503refusals also send aRetry-Afterheader.request_idis on most errors but not all: the forget, erasure, scope, usage and re-derivation routes omit it. The response headerx-request-idis always present; log that one. Send your ownX-Request-Idand a successful response echoes it. An error whose body carries arequest_idminted that id itself and also returns it in a header:x-request-id(for example a404fromGET /v1/events/{id}), orx-cortex-request-idon a401, whosex-request-idstill echoes yours. Since v0.10.2 the same id is on the request's log line and on the audit rows it writes.detailsis present only when the error has any (for exampleINVALID_BODY,IDEMPOTENCY_CONFLICT,ENRICHMENT_OFF,SETTLEMENT_UNAVAILABLE,BLOB_TEXT_ENCODING_UNKNOWN).
Some validation errors are plain text
When the body or query string cannot be parsed into the route's type, some routes answer with a
plain-text message, not the JSON body: an unknown field on /v1/forget (422 Failed to deserialize the JSON body …: unknown field …), a value of the wrong type (facet_plan_override: "x" on
/v1/answer), or a missing required query parameter (GET /v1/events without scope → 400 Failed to deserialize query string: missing field scope). Other routes, such as /v1/recall and /v1/experience,
wrap the same kind of mistake in a JSON INVALID_BODY. Clients should handle a non-JSON error body.
Codes by area
Writes and captures
| Status | Code | When | Retriable |
|---|---|---|---|
| 422 | INVALID_BODY | A malformed body or field (bad scope, bad ?wait= value, unknown recall field); also a field whose capability this server does not list (details.unsupported_fields and details.required_capabilities) | no |
| 422 | INVALID_ENVELOPE | A malformed language, scripts, context.timezone, context.refers_to, or an idempotency_key over 64 characters | no |
| 422 | UNREGISTERED_SCOPE_TYPE | A scope type not in the allowed set (e.g. channel:) on the first write into a new scope | no |
| 409 | IDEMPOTENCY_CONFLICT | The same idempotency_key in the same scope with a different body | no |
| 422 | BLOB_TEXT_ENCODING_UNKNOWN | A blob_ref to a text blob whose bytes decode in no declared or detectable encoding | no |
| 503 | SETTLEMENT_UNAVAILABLE | ?wait=settled on the default single-node server | no |
| 409 | DESTRUCTIVE_OPERATION_IN_PROGRESS | A capture that would register a new scope inside a running forget or erasure, after CORTEX_DESTRUCTIVE_LEASE_WAIT_SECS | yes (Retry-After: 5) |
| 502 | INDEXING_FAILED | Accepted into the WAL but indexing failed; re-send with the same key | yes |
| 408 | WAIT_TIMEOUT | A ?wait=indexed or consolidated capture ran out of its one wait budget (about 30 s; bulk min(30 s + 1 s per item, 600 s)); the write is captured and durable. details has waited_ms, waiting_on and status_url (single) or event_ids (bulk) | yes: poll, or re-send with the same idempotency_key |
Details: Experience.
Reads, recall and answers
| Status | Code | When | Retriable |
|---|---|---|---|
| 404 | NOT_FOUND | An unknown, forgotten, erased or cancelled id (GET /v1/events/{id}, erasure jobs, retention policies); also an event outside GET /v1/events/{id}?scope= | no |
| 422 | INVALID_SCOPE_GRAMMAR | An empty, blank or malformed ?scope= on GET /v1/events/{id} (a repeated scope is a plain-text 400) | no |
| 422 | UNPARSEABLE_TEMPORAL | A time phrase the server cannot parse, in strict mode (natural_mode: "lenient" drops it instead) | no |
| 422 | EVAL_OVERRIDE_DISABLED | facet_plan_override on /v1/answer without the evaluation flag | no |
| 503 | ANSWER_UNAVAILABLE | /v1/answer or /v1/compose with no answer lane configured | no |
| 502 | ANSWER_PROVIDER_ERROR | The answer model's provider refused the call (e.g. a model that rejects max_tokens) | depends on the cause |
Details: Recall, Answer, LLM & Answer.
Forget and erasure
| Status | Code | When | Retriable |
|---|---|---|---|
| 422 | EMPTY_SELECTOR_WITHOUT_CONFIRMATION | An empty selector without confirm_all: true | no |
| 400 | AMBIGUOUS_SELECTOR_CONFIRM_ALL | A selector together with confirm_all: true on /v1/forget | no |
| 422 | UNKNOWN_PREVIEW_ID | A from_preview_id that is unknown, expired, from before a restart, or for a different operation | no |
| 502 | FORGET_BACKEND_FAILED / ERASURE_BACKEND_FAILED | A store could not complete the deletion; nothing is reported as deleted | yes |
| 409 | SCOPE_STATE_CHANGED | A /v1/scopes change inside a running forget's or erasure's footprint; nothing was applied | yes, although the body says retriable: false (v0.10.2): re-send the mutation once the forget or erasure is done |
| 409 | REDERIVE_IN_PROGRESS | A forget, erasure or preview on a scope a re-derivation job holds | yes (Retry-After: 30) |
| 409 | AUTHORIZATION_STATE_CHANGED | Scopes were registered or removed inside a forget's, erasure's or erasure poll's footprint on each of 5 attempts to authorize it; nothing changed | yes (Retry-After: 1) |
| 503 | SERVER_SHUTTING_DOWN | A forget or erasure sent after a graceful stop began; nothing changed | yes (Retry-After: 5) |
Scopes, retention and usage
| Status | Code | When | Retriable |
|---|---|---|---|
| 409 | SCOPE_REGISTRATION_EXISTS | Registering a scope that is already registered | no |
| 422 | INVALID_RETENTION_POLICY | A retention policy with a per-source key that is not source:<system> | no |
| 400 | INVALID_PERIOD / INVALID_ROLE | An unknown period or role on /v1/admin/usage | no |
Details: Scopes, Retention & WAL, Usage ledger.
Authentication and policy
| Status | Code | When | Retriable |
|---|---|---|---|
| 401 | MISSING_TOKEN | No bearer token on a server that requires one (an X-Cortex-Actor header alone is not enough) | no |
| 401 | INVALID_TOKEN_SIGNATURE / TOKEN_EXPIRED | A token that does not verify, or has expired | no |
| 401 | ACTOR_MISMATCH | An X-Cortex-Actor header that does not match the token's subject | no |
| 403 | POLICY_DENIED | The policy stack refuses the capability | no |
| 503 | NOT_CONFIGURED | Token minting (/v1/auth/signup, /v1/auth/tokens) on a keyed server without CORTEX_V1_MINTER_ENABLE=1 | no |
| 429 | RATE_LIMITED | Too many requests | yes |
Details: Authorization, Auth.
Admin and derived-data repair
| Status | Code | When | Retriable |
|---|---|---|---|
| 422 | NO_TARGETS | A re-derivation or unmerge request that names nothing to repair | no |
| 409 | ENRICHMENT_OFF | POST /v1/admin/derived/rederive with enrichment off | no |
| 404 | JOB_NOT_FOUND | An unknown or another caller's re-derivation job | no |
| 409 | JOB_MISMATCH, JOB_NOT_COMPLETED, JOB_ALREADY_CONTINUED | Reusing or continuing a re-derivation job wrongly | no |
| 422 | COLD_BACKUP_ACK_REQUIRED, NO_ELIGIBLE_EVENTS, INVALID_AUDIT | A re-derivation apply without the backup acknowledgement, with nothing eligible, or with an unreadable audit report | no |
| 409 | UNIFIED_WAL_REQUIRED | source-blob-links on the legacy dual WAL | no |
| 503 | REDERIVE_UNSUPPORTED | The coordinator cannot run re-derivation | no |
| 500 | REDERIVE_FAILED, REDERIVE_PLAN_FAILED, UNMERGE_FAILED, SOURCE_BLOB_LINKS_FAILED, ORPHAN_BLOB_REPORT_FAILED, CHUNK_INSPECTION_FAILED | The operator route failed; the message says why | after the cause is fixed |
Details: Admin.
Code plane
| Status | Code | When | Retriable |
|---|---|---|---|
| 503 | CODE_PLANE_DISABLED | Any /v1/code/* route with CORTEX_CODE_PLANE off | no |
| 404 | REPO_NOT_FOUND | An unknown repository, or one you cannot see | no |
| 422 | REPO_PATH_NOT_ALLOWED | A path outside CORTEX_CODE_REPO_ROOTS or always refused (details.reason) | no |
| 422 | REGISTRATION_FAILED | A repository path the server cannot register (e.g. a relative path) | no |
| 403 | SCOPE_REGISTRATION_REQUIRED | Registering a repository directly under a root scope | no |
| 409 | REPO_SCOPE_IN_USE | A scope binding another repository already holds | no |
| 400 | INVALID_PATHS | A malformed certify paths filter | no |
Details: Code Plane.
Blobs
| Status | Code | When | Retriable |
|---|---|---|---|
| 415 | unsupported_media_type | A multipart/form-data upload (send the raw bytes instead) | no |
| 413 | (none) | A body over 32 MiB | no |
| 422 | INVALID_IDEMPOTENCY_KEY | An idempotent blob route without X-Cortex-Idempotency-Key | no |
| 409 | IDEMPOTENCY_CONFLICT | The same blob key with different bytes | no |
Details: Blobs.
Codes that changed in v0.10.2
- A
?wait=capture that runs out answers408 WAIT_TIMEOUTafter about 30 s, not about 60 s, withdetails.waited_msanddetails.waiting_on. A waiting bulk's budget now covers its inline indexing, so it can answer408(withdetails.event_ids) where it used to wait and answer200. - A forget, erasure or erasure poll that raced a scope registration outside its footprint used to get
a spurious
403 POLICY_DENIED(or404onGET /v1/erasures/{id}); it now proceeds. A registration inside the footprint on every attempt gives the retriable409 AUTHORIZATION_STATE_CHANGED. GET /v1/events/{id}?scope=adds422 INVALID_SCOPE_GRAMMARfor a bad value.
Codes that changed in v0.10.1
- A capture that waited on a running forget used to get
403 POLICY_DENIED; it now gets the retriable409 DESTRUCTIVE_OPERATION_IN_PROGRESS. - Forgetting an id stored in another scope used to give
502 FORGET_BACKEND_FAILED; it now answers200with every count 0, like a missing id. - A
?wait=indexedcapture that a concurrent forget blanked is no longer reported as502 INDEXING_FAILED. - Cancelling a finished erasure job answers
404 NOT_FOUND(v0.9.13 answered405).
Server start-up refusals (for example CLUSTER_MODE_UNSUPPORTED, exit code 78) are not HTTP errors;
see Storage & Cluster and Configuration.