Docs index
Errors
Every code the API can return, what it means, and what to do about it.
Branch on the code, not the status
Failures return a machine code alongside the HTTP status. Branch on the code: the status says what kind of problem it is, and the code says which one. The Status column below is filled where one route owns a code; where it is blank the same code is returned with different statuses by different operations, and the authoritative status per operation is in the OpenAPI document under the response it sits in.
Note A 503 means the work did not start. A 409 means the request was understood and the state refused it — those are different retries. A 429 means the work is allowed and the window is full: honour Retry-After where it is sent, and otherwise wait for the next clock minute rather than retrying immediately.
Authentication, scope and plan
Everything that can refuse a request before it reaches the work.
| Code | Status | Meaning | What to do |
|---|---|---|---|
AUTH_REQUIRED | 401 | No credential was presented, or the bearer token is not a key TAVONEL issued. This is what an unauthenticated request to any scoped route returns. | Send Authorization: Bearer <key>. Keys are created in Workspace → Developers and the plaintext is shown once; a key you cannot find again is rotated, not recovered. |
API_KEY_INVALID | varies | The bearer token was well-formed but did not match a stored key. | Check you are sending the whole key, including the tvnl_live_ prefix, and that it belongs to TAVONEL. |
API_KEY_EXPIRED | varies | The key matched and its expiry has passed. | Rotate it: POST /developer/keys/{id}/rotate in a signed-in session returns a replacement once. |
API_KEY_REVOKED | varies | The key matched and was revoked. A rotation revokes the key it replaces. | Use the replacement key from the rotation, or mint a new one. |
API_SCOPE_REQUIRED | 403 | The key authenticated and does not carry the scope this operation requires. The request was refused rather than answered with less. | Mint a key carrying the scope named in the operation's x-tavonel-scope. Scopes are fixed at creation; a key is not widened in place. |
PILOT_ACCESS_REQUIRED | 403 | The credential is valid and the workspace it names is not admitted to TAVONEL. | Nothing a caller can send fixes this. Access is arranged with us — write to support@tavonel.com. |
WORKSPACE_MEMBERSHIP_REQUIRED | 403 | The user is authenticated but has no active durable membership in the requested workspace. | Accept a valid workspace invitation, or ask a workspace owner to restore membership before retrying. |
API_KEY_AUTHORIZATION_REVOKED | 403 | The API key was issued under an older membership authority revision and cannot regain access after that grant changes. | Create a new key after the current membership is confirmed; an old key cannot be revived. |
AUTHORIZATION_CHANGED_RETRY | 403 | The caller's authorization changed while the request was in flight, so the request was abandoned rather than finished under a permission that may no longer hold. | Retry once. A second occurrence means the permission really was removed. |
SUBSCRIPTION_REQUIRED | varies | The workspace has no plan that includes this operation. | See /pricing for which plan includes it. |
STUDIO_SUBSCRIPTION_REQUIRED | 402 | The operation needs the higher plan tier — activation, rollback and retrieval-index rebuild are the three. | See /pricing. The bar is the same for all three on purpose: a plan that may activate and may not rebuild would leave its own Worlds answering from the fallback. |
SELF_SERVICE_NOT_ENABLED | varies | Self-serve purchase is not open today. | Nothing a caller can send changes it. Access is arranged with us. |
GPU_CREDITS_REQUIRED | varies | The workspace has no processing balance left to reserve against. | Add pages, or wait for the next grant. Nothing was charged. |
Rate and concurrency
The two limits the deployment enforces, and the one case where a refusal means the limit could not be read.
| Code | Status | Meaning | What to do |
|---|---|---|---|
API_RATE_LIMITED | 429 | The key has used its per-minute allowance for this scope. The window is a fixed clock minute per key and scope; the allowances are on /docs/billing-and-limits. | Wait for the next clock minute and retry. No Retry-After header is sent today, so back off on your own clock — a fixed one-minute wait is enough by construction. |
API_RATE_LIMIT_UNAVAILABLE | 503 | The allowance could not be read, so the request was refused rather than run unbounded. | Retry. This is fail-closed behaviour, not a limit you hit. |
ACTIVATION_RATE_LIMITED | 429 | The workspace has used its hour's allowance of World activations, rollbacks or retrieval-index rebuilds. Nothing was charged. | Honour the Retry-After header — it carries the seconds until the oldest one leaves the window — rather than retrying immediately. |
ACTIVATION_RATE_LIMIT_UNAVAILABLE | 503 | That hourly allowance could not be read, so the request was refused rather than run unbounded. | Retry. This is fail-closed behaviour, not a limit you hit. |
INTAKE_RATE_LIMITED | 429 | Too many upload capabilities were requested in the window. | Honour Retry-After (60 seconds) and retry. |
INTAKE_DAILY_QUOTA_EXCEEDED | 429 | The workspace's daily intake quota is spent. | Honour Retry-After (3600 seconds); the quota rolls with the day. |
WORKSPACE_CONCURRENCY_LIMIT | 429 | The workspace already has as many compiles in flight as it may. | Wait for one to settle. Poll GET /compile-jobs to see which. |
COMPILE_JOB_WORKSPACE_LIMIT_REACHED | varies | The workspace is at its compile-job ceiling. | Let running jobs settle before starting another. |
WORKSPACE_CACHE_CAPACITY_LIMIT | varies | The workspace's cached working set is full. | Retry after current work settles. |
Request shape
Refusals decided from the request alone, before any state is read.
| Code | Status | Meaning | What to do |
|---|---|---|---|
INVALID_JSON | 400 | The body was not parseable JSON. | Send valid JSON and content-type: application/json. |
NOT_JSON | 400 | The request did not declare a JSON body. | Set content-type: application/json. |
METADATA_ONLY_ENDPOINT | 415 | Document bytes were POSTed to a route that accepts only metadata. Bytes go direct to storage, never through the application server. | Request an upload capability and PUT the bytes to the URL it returns. |
REQUEST_TOO_LARGE | 413 | The JSON body exceeded the route's bound. | Split the request. The bounds are per-route and stated in the operation. |
JSON_TOO_LARGE | 413 | The JSON body exceeded the shared parse bound. | Split the request. |
UNQUALIFIED_INPUT | 400 | A required field was missing or was not of the declared type. | Check the request schema for the operation; the response names no field, so validate against the contract. |
LIMIT_INVALID | 400 | limit was outside the range the operation accepts. | Use a limit inside the bounds the operation declares. |
AUDIT_LIMIT_INVALID | 400 | The audit limit was outside its range. | Use a limit inside the declared bounds. |
RUN_ID_REQUIRED | 400 | The run id path segment was empty. | Supply the run id returned when the run was started. |
RESOLUTION_REQUIRED | 400 | A blocker resolution was requested with no resolution field. | Send one of continue, remove_blocked, retry_eligible. |
RESOLUTION_NOT_APPLIED | 409 | The resolution was understood and the job's state refused it. | Re-read the job: the blockers it is holding decide which resolutions are legal. |
COLLECTION_ID_INVALID | 400 | The collection id did not match collection-<32 hex>. | Use the id as returned, unmodified. |
WORLD_ID_INVALID | 400 | The World id did not match the collection id pattern. | Use the collection id as returned. |
CORPUS_ID_INVALID | 400 | The corpus id did not match corpus-<32 hex>. | Use the corpus id as returned. |
CONNECTION_ID_INVALID | 400 | The connection id was not a UUID. | Use the id from GET /connections. |
OAUTH_CONNECTION_ID_INVALID | 400 | The OAuth connection id was not a UUID. | Use the id from GET /oauth-connectors. |
API_KEY_ID_INVALID | 400 | The key id was not a UUID. | Use the key id shown in Workspace → Developers. |
API_KEY_INPUT_INVALID | 400 | The rotation body did not validate. | Check the request schema for the operation. |
MANIFEST_DIGEST_INVALID | 400 | The manifest digest did not match sha256:<64 hex>. | Pass the digest exactly as the World reported it, prefix included. |
SOURCE_IDEMPOTENCY_KEY_INVALID | 400 | x-tavonel-source-idempotency-key was present and was not 64 hex characters. | Send a sha256 hex digest, or omit the header entirely. |
QUESTION_INVALID | 400 | The question was absent, or shorter than the minimum. | Send a question between 3 and 500 characters. |
QUESTION_TOO_LARGE | 413 | The question exceeded 500 characters. | Shorten it. Ask is a question, not a document. |
QUERY_INVALID | 400 | The search query was absent, or shorter than the minimum. | Send a query between 3 and 500 characters. |
QUERY_TOO_LARGE | 413 | The search query exceeded 500 characters. | Shorten it. |
RETRIEVAL_QUESTION_INVALID | 400 | The retrieval runtime refused the question text itself. | Send a plain question; control characters and empty strings are refused. |
WORLD_PAGE_LIMIT_INVALID | 400 | limit was outside 1–50. | Use a limit inside 1–50, or omit it to read the whole lens. |
WORLD_PAGE_CURSOR_INVALID | 400 | The cursor named an id this lens does not contain. A wrong cursor is refused rather than answered with an empty page, so a paging bug is visible instead of silent. | Restart from the first page. The cursor is the last item id from the previous page, keyset — not an offset. |
WORLD_LENS_NOT_PAGEABLE | 400 | history, files and review return whole; limit and cursor are refused on them. | Drop the paging parameters for those three lenses. |
WORLD_PROMOTION_INVALID | 400 | The activation request body did not validate. | Activation is a signed-in browser action; no API key holds it. |
WORLD_ROLLBACK_INVALID | 400 | The rollback request body did not validate. | Rollback is a signed-in browser action; no API key holds it. |
PROMOTION_METADATA_TOO_LARGE | 413 | The activation note exceeded its bound. | Shorten the note. |
ROLLBACK_METADATA_TOO_LARGE | 413 | The rollback note exceeded its bound. | Shorten the note. |
CONNECTION_INPUT_INVALID | 400 | The connection body did not match ConnectionInput. | Check the schema: provider, mode, displayName and configuration are required and secretReference must be null. |
CONNECTION_BATCH_INVALID | 400 | The sync batch did not match ConnectionBatch. | Check the schema. Every event needs kind, nativeId, revision, contentSha256, sizeBytes, mimeType, documentId and sourceIdempotencyKey, each nullable where the schema says so. |
OAUTH_CONNECTOR_INPUT_INVALID | 400 | The authorization body did not match OAuthConnectorAuthorizationInput. | Send a supported provider and a displayName. |
OAUTH_SYNC_INPUT_INVALID | 400 | The OAuth connection sync body did not validate. | Check the schema for the operation. |
AUDIT_EXPORT_WINDOW_INVALID | 400 | The audit export window was not a range TAVONEL serves. | Narrow the window. |
Documents and intake
What stops a file before it becomes a source — including the two ceilings the deployment actually enforces.
| Code | Status | Meaning | What to do |
|---|---|---|---|
SOURCE_EXCEEDS_PROCESSING_CEILING | 413 | requestedBytes is above what every processor in the chain can read. The body carries maxBytes, maxPages and a limit sentence. This is the refusal the 5 MB per-source ceiling produces. | Split the document and upload the parts. Admitting a larger file would only move the refusal somewhere you cannot see it. |
SOURCE_TOO_MANY_PAGES | varies | The document decoded to more pages than the rasterizer renders. The page ceiling cannot be checked at intake, because intake deliberately never decodes the document, so this arrives after the bytes are stored rather than at the capability call. | Split the document. The page ceiling is published on /docs/files-and-formats and in the capability manifest's knownLimitations. |
SOURCE_TOO_LARGE | varies | A connected source's bytes are above the per-source ceiling. | Exclude it from the sync, or split it at the source. |
INTAKE_FILE_TOO_LARGE | varies | The admission ledger refused the size. Answered to the caller as SOURCE_EXCEEDS_PROCESSING_CEILING. | Split the document. |
INTAKE_DISABLED | 503 | Intake is closed today. | Nothing a caller can send opens it. /api/status publishes the current state. |
INTAKE_IDEMPOTENCY_CONFLICT | 409 | The same source idempotency key was already used for different bytes. | Use a fresh key, or re-send the original bytes. |
UNQUALIFIED_DOCUMENT | 400 | The document id does not name a document in this workspace, or it is not in a state this operation accepts. | List documents and use an id from the response. |
UNQUALIFIED_MIME | 400 | The declared MIME type is not in the capability manifest. | Read GET /capabilities: a format absent from it is refused at upload rather than accepted and dropped. |
MIME_TYPE_UNSUPPORTED | 400 | Same refusal, raised by the qualifier. | Check GET /capabilities before uploading. |
FILENAME_MIME_MISMATCH | 400 | The extension and the declared MIME type disagree. | Declare the MIME type that matches the extension. |
FILE_NAME_INVALID | 400 | The filename carried a path separator or a character the store refuses. | Send a plain filename with no directory component. |
INVALID_FILENAME | 400 | Same refusal from the intake validator. | Send a plain filename. |
SIGNER_NOT_CONFIGURED | 503 | The upload URL signer is not configured, so no capability can be issued. | Nothing a caller can send. /api/status reports the deployment's state. |
SOURCE_VERSION_AMBIGUOUS | 409 | Two source versions carry the same identity and the request did not say which. | Name the version explicitly. |
SOURCE_NOT_QUALIFIED | varies | A connected source did not qualify for compilation. | Check the source's format against GET /capabilities. |
SOURCE_REVOKED | varies | The source's connection was revoked; its bytes are no longer reachable. | Reconnect the source, or drop it from the set. |
SOURCE_TOMBSTONED | varies | The source was deleted at origin and is recorded as gone rather than silently omitted. | Remove it from the compile set. |
TRIAL_FILE_TOO_LARGE | 413 | Above the free-evaluation per-file bound, which is lower than the deployment ceiling. | The body carries maxBytes. Split the file, or move to a paid plan. |
TRIAL_ARCHIVE_NOT_INCLUDED | 402 | ZIP upload is not included in the free evaluation. | Upload the files individually, or move to a paid plan. |
TRIAL_FILE_LIMIT_EXCEEDED | varies | The free evaluation's file count is spent. | Move to a paid plan. |
TRIAL_PAGE_LIMIT_EXCEEDED | varies | The free evaluation's page allowance is spent. | Move to a paid plan. |
TRIAL_NOT_ACTIVE | varies | No free evaluation is active for this workspace. | Move to a paid plan. |
TRIAL_DISABLED | varies | Free evaluation is closed today. | Nothing a caller can send. |
TRIAL_CAPACITY_REACHED | varies | The deployment's concurrent free evaluations are full. | Retry later, or move to a paid plan. |
TRIAL_FEATURE_NOT_INCLUDED | 402 | The operation — key rotation among them — is not in the free evaluation. | Move to a paid plan. |
TRIAL_WORLD_LIMIT_REACHED | 402 | The free evaluation's compiled-World count is spent. | Move to a paid plan. |
TRIAL_DURABLE_COMPILE_REQUIRED | 402 | The free evaluation compiles through the durable job route only. | Use POST /compile-jobs rather than POST /collections/compile. |
TRIAL_SOURCE_REVIEW_REQUIRED | varies | A free-evaluation source was held for review by the risk gate. | Nothing automatic clears it. Write to support@tavonel.com. |
Compile
The document set, the job, and what a partial failure does.
| Code | Status | Meaning | What to do |
|---|---|---|---|
DOCUMENT_IDS_REQUIRED | 400 | The request carried no document id array. | Send documentIds with at least one id. |
DOCUMENT_SET_EMPTY | 400 | Nothing was selected to compile. | Send at least one document id. |
DOCUMENT_SET_UNQUALIFIED | 400 | A value in documentIds was not a document id. | Send UUIDs as returned by GET /documents. |
DOCUMENT_SET_TOO_LARGE | 400 | More documents than one compile carries were sent to the single-compile route. | Use POST /compile-jobs, which partitions a larger selection into parts server-side. |
CORPUS_TOO_LARGE | 400 | More documents than one run carries. | Split the selection across runs. |
SPLIT_PART_LIMIT_EXCEEDED | varies | Partitioning the selection would make more parts than a run holds. | Split the selection across runs. |
COMPILE_JOB_NOT_FOUND | 404 | No such job in this workspace. Job ids are workspace-scoped, so this is also the answer for another tenant's id. | List GET /compile-jobs to recover the id. |
COMPILE_JOB_SCOPE_INVALID | varies | The job exists and belongs to another workspace. | Use a job from your own workspace. |
COMPILE_JOB_ALREADY_SETTLED | 409 | The job had already finished. Nothing was discarded — a cancel arriving a second after a compile finished does not destroy the result. | Read the job: it is terminal, and its result stands. |
COMPILE_JOB_SLOT_CONFLICT | 409 | A part of this corpus is already held by a job over a different document set. Retrying does not clear it. | Let the holding job settle, or cancel it, before resubmitting. |
OCR_NOT_READY | 409 | The sources have not finished being read. | Retry rather than fail. Poll the job's events until reading completes. |
SECURITY_BLOCKER_REQUIRES_EXPLICIT_REMOVAL | 409 | continue was sent while a source was held by a safety check. | Use remove_blocked, which records who removed it. continue will not step over a security blocker, because a pipeline that learns to skip security stops has stopped being one. |
CORE_NOT_CONFIGURED | 503 | The compile runtime is unavailable. The request was not charged. | Retry. /api/status reports the deployment's state. |
CORE_UNAVAILABLE | 503 | The compile runtime was reachable and did not answer. | Retry. Nothing was charged. |
CORE_REQUEST_INVALID | varies | The compile runtime refused the request it was handed. | Nothing a caller can send. Report it with the job id. |
CORE_RECEIPT_INVALID | varies | The compile runtime answered with a receipt that did not validate, so the result was refused rather than stored. | Retry. Fail-closed by design: a result that cannot be validated is not a smaller result. |
COMPILE_JOB_STORE_NOT_CONFIGURED | 503 | The durable job store is not configured. | Nothing a caller can send. |
COMPILE_JOB_STORE_READ_FAILED | 503 | The durable job store could not be read. | Retry. |
COMPILE_JOB_STORE_WRITE_FAILED | 503 | The durable job store could not be written, so the intent was not recorded. | Retry. Submitting the same set again converges on one job. |
COMPILE_JOB_RPC_UNDEFINED | 503 | The job store is missing a procedure this build expects — a deployment mismatch. | Nothing a caller can send. Report it. |
Worlds, retrieval and answers
Reading a World, and what Search and Ask refuse rather than weaken.
| Code | Status | Meaning | What to do |
|---|---|---|---|
ACTIVE_WORLD_NOT_FOUND | 409 | Nothing has been activated for this collection, so there is no World to read, index or answer from. A candidate nobody accepted is not a smaller answer — it is a different one. | Promote a candidate in a signed-in session. No key can promote. |
ACTIVE_WORLD_CONFLICT | 409 | Two activations raced; neither was applied silently. | Re-read the World and retry the activation. |
WORLD_TRANSITION_IDEMPOTENCY_CONFLICT | 409 | The transition idempotency key was already bound to a different activation or rollback request. | Reuse the key only for the identical request, or generate a fresh key after re-reading the active World. |
WORLD_VERSION_BINDING_CONFLICT | 409 | The requested transition was bound to a World revision that is no longer current. | Re-read the active World and submit the transition against its current revision. |
WORLD_TRANSITION_FORBIDDEN | 403 | The transition was refused because the current principal may no longer change this World. | Refresh your session and workspace membership; an owner or admin must perform the transition. |
ACTIVE_WORLD_CHANGED_RETRY | 409 | The active version changed while the request was in flight. | Retry. The answer would have mixed two versions. |
ACTIVE_WORLD_ARTIFACT_INVALID | 422 | The activated artifact failed validation on read, so it was refused rather than served partially. | Recompile. Report it with the manifest digest. |
ACTIVE_WORLD_BINDING_INVALID | varies | The active pointer names a version the store cannot resolve. | Report it with the collection id. |
ACTIVE_WORLD_RETRIEVAL_INVALID | varies | The active World's retrieval state did not validate. | Rebuild the index with POST /collections/{id}/retrieval-index. |
ACTIVE_WORLD_STORE_UNAVAILABLE | 503 | The World store could not be reached. | Retry. |
WORLD_NOT_FOUND | 404 | No World for that collection id in this workspace. | Check the id, and that it belongs to your workspace. |
WORLD_LENS_NOT_FOUND | 404 | The lens name is not one of objects, relations, evidence, history, files, review. | Use one of the six. |
WORLD_READ_MODEL_INVALID | 422 | The read model failed validation, so nothing was served. | Report it with the manifest digest. |
WORLD_STORE_NOT_CONFIGURED | 503 | The World store is not configured. | Nothing a caller can send. |
WORLD_STORE_READ_FAILED | 503 | The World store could not be read. | Retry. |
WORLD_STORE_WRITE_FAILED | 503 | The World store could not be written. | Retry. |
WORLD_VERSION_BINDING_INVALID | varies | A retained version could not be bound to its manifest. | Report it with the collection id. |
WORLD_CANDIDATE_NOT_PROMOTABLE | 422 | The candidate failed the checks activation requires. | Read the validation report in the candidate; it names what failed. |
WORLD_CANDIDATE_SOURCE_BINDING_INVALID | 422 | A candidate object cites a source version the store cannot resolve. | Recompile. A World with an unresolved link is not emitted. |
WORLD_EQUIVALENCE_REFUSED | 409 | The activation would have replaced the active World with one the equivalence check does not accept as the same subject. | Review the diff before promoting. |
EVIDENCE_DANGLING | varies | An object cites evidence that is not in the package. | Recompile. Fail-closed: the World is not emitted rather than emitted incomplete. |
ROLLBACK_TARGET_NOT_FOUND | 404 | The version to roll back to is not retained. | Read the history lens for the versions that are. |
ROLLBACK_TARGET_CONFLICT | 409 | The rollback target no longer matches the retained history or active revision used to authorize the request. | Re-read the World history and choose a target from the current response. |
PROMOTION_ROLE_REQUIRED | 403 | Activation needs the workspace owner or admin role. | Ask an owner or admin to activate. |
ROLLBACK_ROLE_REQUIRED | 403 | Rollback needs the workspace owner or admin role. | Ask an owner or admin to roll back. |
RETRIEVAL_COMPILE_ROLE_REQUIRED | 403 | Rebuilding the retrieval index needs the workspace owner or admin role, on top of the scope. | Ask an owner or admin to rebuild. |
RETRIEVAL_RUN_NOT_FOUND | 409 | The active World has no completed retrieval compile run, so there is no queryable index. Search has no fallback — a weaker answer presented as the real one is worse than a refusal you can act on. | POST /collections/{id}/retrieval-index rebuilds it. The body carries retrievalIndex and retrievalNotice saying which state it is in. |
RETRIEVAL_PROFILE_NOT_FOUND | 409 | The retrieval profile the index was compiled against is not registered. | Rebuild the index. |
RETRIEVAL_INDEX_NOT_COMPILED | 503 | A rebuild did not reach a queryable index. retrievalIndex.errorClass names the failure class. Never answered as a 200. | Read errorClass, then retry. An embedder outage and an empty corpus are different problems. |
RETRIEVAL_RUNTIME_UNAVAILABLE | 503 | The exact retrieval runtime bound to this compiled index is unavailable or failed its identity checks, so the request was refused instead of silently using a different model. | Retry after the configured runtime is restored, or rebuild the index against an available registered profile. |
RETRIEVAL_COMPILE_NO_UNITS | varies | The World produced no retrievable units, so there was nothing to index. | Check the compile: a World with no evidence has nothing to retrieve. |
RETRIEVAL_COMPILE_EMBEDDING_PROVIDER_FAILED | varies | The embedding provider failed during the rebuild. | Retry. Dense retrieval is skipped rather than faked when no embedder is configured — see degradations. |
RETRIEVAL_COMPILE_EMBEDDING_WRITE_FAILED | varies | Embeddings could not be written. | Retry. |
RETRIEVAL_COMPILE_UNIT_WRITE_FAILED | varies | Retrieval units could not be written. | Retry. |
RETRIEVAL_COMPILE_PROFILE_REGISTRATION_FAILED | varies | The retrieval profile could not be registered. | Retry. |
RETRIEVAL_COMPILE_RUN_REJECTED | varies | The rebuild run was refused before it started. | Check the active World exists and the plan bar is met. |
RETRIEVAL_COMPILE_SOURCE_BINDING_UNRESOLVED | varies | A retrieval unit cited a source version that could not be resolved, so the index was refused rather than compiled with a dangling citation. | Recompile the World. |
Review and evidence
The append-only decision record, and what it revalidates before it writes.
| Code | Status | Meaning | What to do |
|---|---|---|---|
REVIEW_REQUEST_INVALID | 400 | The review body did not validate. | Check the schema: collectionId, manifestDigest, evidenceId, action and an 8–1000 character reason are all required. |
REVIEW_REQUEST_TOO_LARGE | 413 | The review body exceeded its bound. | Shorten the reason. |
REVIEW_EVIDENCE_NOT_FOUND | 404 | The evidence id is not in the World version named by the digest. | Read the evidence lens for that manifest digest and use an id from it. |
REVIEW_WORLD_CHANGED | 409 | The World changed between reading the evidence and recording the decision, so the decision was not written against a version it may not describe. | Re-read the evidence at the current digest and decide again. |
REVIEW_PATCH_INVALID | 400 | An Edit decision carried a patch that did not validate. | Check the patch shape against the evidence you read. |
REVIEW_RECEIPT_INVALID | varies | The decision receipt did not validate, so nothing was recorded. | Retry. Nothing partial was written. |
REVIEW_STORE_NOT_CONFIGURED | 503 | The review store is not configured. | Nothing a caller can send. |
REVIEW_STORE_READ_FAILED | 503 | The review store could not be read. | Retry. |
REVIEW_STORE_WRITE_FAILED | 503 | The review store could not be written. | Retry. The record is append-only, so a retry does not overwrite. |
PATCH_BEFORE_MISMATCH | 409 | The patch's before value does not match what is stored now. | Re-read the target and rebuild the patch. |
PATCH_TARGET_NOT_FOUND | 404 | The patch names a target the World does not contain. | Re-read the World. |
PATCH_TARGET_NOT_EDITABLE | 409 | The target is not one a review decision may edit. | Accept or Reject instead. |
PATCH_NO_CHANGE | 400 | The patch would change nothing. | Send a patch that differs, or record Accept. |
PATCH_LABEL_INVALID | 400 | The patch label did not validate. | Check the label bounds. |
PATCH_ARTIFACT_INVALID | 422 | Applying the patch would produce an artifact that does not validate. | Fail-closed by design. Narrow the edit. |
REJECT_RECEIPT_INVALID | varies | A Reject decision's receipt did not validate. | Retry. |
Exports and packages
Signed, or refused. There is no third outcome.
| Code | Status | Meaning | What to do |
|---|---|---|---|
EXPORT_SIGNER_NOT_CONFIGURED | 503 | No signing key is configured, so neither a signed archive nor a fingerprint can be produced. GET /export/trust answers this rather than a fingerprint nobody can verify against. | Nothing a caller can send. A deployment without a signer cannot hand out an archive at all — that is the contract, not an outage. |
EXPORT_SIGNER_INVALID | 503 | The configured signer did not produce a usable key. | Nothing a caller can send. Report it. |
SIGNATURE_READ_FAILED | 503 | The detached signature could not be read. | Retry the download. |
INVALID_SIGNATURE | 422 | The signature did not verify against the key it names. | Do not trust the archive. Re-download, and verify against the fingerprint from GET /export/trust rather than one inside the archive. |
COLLECTION_PACKAGE_INVALID | 422 | The package failed its own validation, so it was not served. | Recompile. A package whose file digests do not match is not served. |
COLLECTION_KEY_INVALID | 400 | The package object key did not validate. | Report it with the collection id. |
COLLECTION_SOURCE_BINDING_INVALID | 422 | A package entry cites a source version that cannot be resolved. | Recompile. |
COLLECTION_JSON_PREFIX_REQUIRED | varies | The package store is configured with a prefix the request did not use. | Nothing a caller can send. Report it. |
R2_NOT_CONFIGURED | 503 | Object storage is not configured. | Nothing a caller can send. |
DELETION_NOT_PROVEN | varies | A delete was requested and the store could not prove it happened, so success was not reported. | Retry. Fail-closed: an unproven delete is not reported as a delete. |
RESTORE_NOT_PROVEN | varies | A restore could not be proven. | Retry. |
Connections and connectors
Durable cursors, and the refusals that keep two syncs from disagreeing.
| Code | Status | Meaning | What to do |
|---|---|---|---|
CONNECTION_NOT_FOUND | 404 | No such connection in this workspace. | List GET /connections. |
CONNECTION_NOT_SYNCABLE | 409 | The connection is revoked or in a state that does not accept a batch. | Re-create the connection. |
CONNECTION_CREATE_FAILED | 503 | The connection could not be recorded. | Retry. |
CONNECTION_REVOKE_FAILED | 503 | The revoke could not be recorded, so it is not reported as done. Immutable outputs are retained by design when a revoke does succeed. | Retry. A revoke that cannot be proven is not reported as a revoke. |
CONNECTION_BATCH_CONFLICT | 409 | The batch's previousCursorSha256 does not match the committed cursor — another sync moved it. | Re-read the connection's cursor and rebuild the batch from it. |
CONNECTION_CURSOR_CONFLICT | 409 | Two batches tried to advance the same cursor. | Re-read the cursor and retry. Replaying the identical batch is idempotent. |
CONNECTION_BATCH_FAILED | 503 | The batch could not be applied. | Retry with the same batchId: an identical replay is idempotent, not a second apply. |
SOURCE_CURSOR_STALE | 409 | The cursor the batch carries is behind the committed one. | Re-read the cursor and collect from there. |
SOURCE_CURSOR_TOO_LARGE | 413 | The cursor value exceeded its bound. | Report it with the connection id. |
SOURCE_DIGEST_REQUIRED | 400 | An event carried bytes with no contentSha256. | Compute the digest at the source. An event without one cannot be bound to a document. |
SOURCE_DIGEST_MISMATCH | 409 | The bytes do not hash to the digest the event declared. | Re-read the file and recompute the digest. |
SOURCE_DIGEST_CONFLICT | 409 | Two events declare different digests for the same revision. | Re-collect the revision. |
SOURCE_IDENTITY_INVALID | 400 | The event's nativeId/revision pair did not validate. | Check the ConnectionEvent schema bounds. |
SOURCE_REVISION_MISMATCH | 409 | The revision the event names is not the one recorded. | Re-read the cursor and collect again. |
SOURCE_VERSION_CHANGED | 409 | The source version changed under the batch. | Retry from the committed cursor. |
SOURCE_VERSION_DIGEST_CONFLICT | 409 | A version is already recorded with a different digest. | Re-collect the version. |
SOURCE_METADATA_INVALID | 400 | Event metadata did not validate. | Check the ConnectionEvent schema. |
SOURCE_LIFECYCLE_REVIEW_REQUIRED | 409 | A lifecycle transition on this source needs a person. | Nothing automatic clears it. |
SOURCE_DOWNLOAD_FAILED | 503 | The agent could not read the source's bytes. | Check the agent's credentials and the source's availability. |
CONNECTOR_SOURCE_ACCESS_DENIED | 401 | The provider refused the stored credential. | Re-authorize the connector. |
CONNECTOR_SOURCE_ACCESS_UNAVAILABLE | 503 | The provider could not be reached. | Retry. |
CONNECTOR_SOURCE_SUSPENSION_UNRESOLVED | 409 | The connector is suspended and the suspension has not been cleared. | Re-authorize, or revoke and re-create the connection. |
CONNECTOR_BINDING_INVALID | varies | The connector binding did not validate. | Re-create the connection. |
CONNECTOR_BINDING_CONFLICT | 409 | Two bindings claim the same connector. | Revoke one. |
OAUTH_PROVIDER_NOT_CONFIGURED | 503 | The provider's client is not configured. Fails closed rather than starting an authorization that cannot complete. | Check /integrations for which providers are live here. |
OAUTH_SECRET_BROKER_NOT_CONFIGURED | 503 | The managed secret broker is not configured, so no refresh secret could be stored — or, on a revoke, deleted. | Nothing a caller can send. |
OAUTH_AUTHORIZATION_START_FAILED | 503 | The single-use PKCE authorization could not be created. | Retry. |
OAUTH_AUTHORIZATION_INVALID | 400 | The authorization is expired, already used, or does not match this workspace. Authorizations are single-use by design. | Start a new authorization. |
OAUTH_PROVIDER_DENIED | 403 | The user declined at the provider. | Start the authorization again. |
OAUTH_PROVIDER_INVALID | varies | The callback path named a provider TAVONEL does not have a connector for. Carried back on the workspace redirect rather than as a JSON body. | Start the authorization from POST /oauth-connectors/authorize; do not construct the callback URL yourself. |
OAUTH_CALLBACK_INVALID | 400 | The callback did not carry a state TAVONEL issued. | Start the authorization again; do not construct the callback yourself. |
OAUTH_CALLBACK_FAILED | 503 | The callback could not be completed. | Start the authorization again. |
OAUTH_CONNECTION_NOT_FOUND | 404 | No such OAuth connection in this workspace. | List GET /oauth-connectors. |
OAUTH_SECRET_REVOCATION_FAILED | 503 | The refresh secret could not be deleted, so the revoke was not reported as done. | Retry. A revoke is reported only when the secret is gone. |
OAUTH_TOKEN_REFRESH_FAILED | 503 | The provider refused the refresh token. | Re-authorize the connector. |
OAUTH_SOURCE_TARGET_UNSUPPORTED | 400 | The requested source target is not one this connector reads. | Check /integrations for what each provider covers here. |
OAUTH_STORE_NOT_CONFIGURED | 503 | The OAuth store is not configured. | Nothing a caller can send. |
OAUTH_STORE_UNAVAILABLE | 503 | The OAuth store could not be reached. | Retry. |
JOB_NOT_FOUND | 404 | No such run in this workspace. | Use a run id from the response that started it. |
JOB_SCOPE_INVALID | 404 | The run exists and belongs to another workspace. Answered as not found rather than as forbidden, so an id cannot be probed across tenants. | Use a run from your own workspace. |
JOB_SYNC_CONFLICT | 409 | Two syncs raced on the same run. | Retry. |
JOB_CONNECTION_MISSING | 409 | The run names a connection that no longer exists. | Re-create the connection. |
JOB_CONNECTION_UNAVAILABLE | 503 | The run's connection could not be read. | Retry. |
JOB_CURSOR_INVALID | 400 | The run's cursor did not validate. | Restart the sync from the committed cursor. |
JOB_STORE_READ_FAILED | 503 | The run store could not be read. | Retry. |
JOB_STORE_WRITE_FAILED | 503 | The run store could not be written. | Retry. |
Keys and audit
Management routes, which no API key can call — they take a signed-in browser session.
| Code | Status | Meaning | What to do |
|---|---|---|---|
API_KEY_NOT_FOUND | 404 | No such key in this workspace. | List the keys in Workspace → Developers. |
API_KEY_CREATE_FAILED | 503 | The replacement key could not be written, so nothing was revoked either. | Retry. Rotation is atomic: either both happened or neither did. |
API_KEY_ROTATE_FAILED | 503 | The rotation could not be completed. | Retry, then check which keys exist before assuming either outcome. |
API_KEY_REVOKE_FAILED | 503 | The revoke could not be recorded. | Retry. A revoke is reported only when it is written. |
DEVELOPER_STORE_NOT_CONFIGURED | 503 | The developer store is not configured. | Nothing a caller can send. |
DEVELOPER_STORE_READ_FAILED | 503 | The developer store could not be read. | Retry. |
DEVELOPER_STORE_BINDING_INVALID | 503 | A stored row did not validate, so it was refused rather than returned partially. | Report it. |
DEVELOPER_AUDIT_READ_FAILED | 503 | The audit trail could not be read. | Retry. |
DEVELOPER_AUDIT_WRITE_FAILED | 503 | An audit event could not be written, so the action it describes was refused. Every key create, rotate and revoke writes an audit row or does not happen. | Retry. |
Generic
Codes that are deliberately vague, and what that vagueness means.
| Code | Status | Meaning | What to do |
|---|---|---|---|
NOT_FOUND | varies | The addressed resource does not exist in this workspace. Deliberately uniform across tenants: the same answer for an id that is not yours and an id that is nobody's. | Check the id, and that it belongs to the workspace the key names. |
READ_FAILED | 503 | A backing store could not be read. | Retry. |
DELETE_FAILED | 503 | A delete could not be completed. | Retry. |
IDEMPOTENCY_KEY_INVALID | 400 | The idempotency key did not match the expected shape. | Send a sha256 hex digest, or omit the header. |
IDEMPOTENCY_CONFLICT | 409 | The same idempotency key was used for a different request body. | Use a fresh key, or re-send the original body. |
IDEMPOTENCY_IN_PROGRESS | 409 | A request with this key is still running. | Poll rather than resubmit. |
COMPUTE_IDEMPOTENCY_CONFLICT | 409 | The processing reservation ledger already holds a different reservation under this request's idempotency key. | Use a fresh source idempotency key, or re-send the original request unchanged. Nothing was charged twice. |
CONTENT_LENGTH_INVALID | 400 | content-length was absent or unparseable where the route requires it. | Send an accurate content-length. |
CONTENT_LENGTH_MISMATCH | 400 | The body length did not match the declared content-length. | Send an accurate content-length. |
Two numbers a client branches on
Note Two numbers this page used to state in prose, for anyone who arrived looking for them: a run carries at most 128 documents and one compile at most 12, and the hourly allowance on World activations, rollbacks and retrieval-index rebuilds is 20 of each. Both are imported from the modules that enforce them, so the page cannot quote a limit the code does not hold.
API version 2026-09-02.1 · reviewed 11 September 2026
Something here out of date or wrong? Report an issue with this page.