Skip to Content

Errors and retries

Error response format

An error always arrives in this envelope.

{ "error": { "code": "RATE_LIMITED", "message": "Request limit exceeded", "retryable": true, "request_id": "0b7b8f0e-0000-4000-8000-000000000010", "details": {"reset_at": "2026-10-05T09:01:00+00:00"} } }
FieldDescription
codeA machine readable error code. Branch on this value, not on message.
messageA human readable English description. It may change.
retryableThe server’s hint on whether sending the same request again can succeed
request_idThe ID of this request. Quote it when you contact us.
detailsExtra information that depends on the code, for example reset_at and state_url
  • Every response has an X-Request-Id header equal to request_id in the body. The server issues a new one for each request.
  • 429 and some 503 responses have a Retry-After header (seconds).
  • Every response is Cache-Control: no-store.
  • You can send X-Correlation-Id for tracing on your side. It is separate from the server generated request_id.
  • A Turn or Run that was accepted (202) and fails later is not an HTTP error. You read it from error.code in the detail response.

Retry principles

  1. Retry only errors whose retryable is true and network errors (dropped connection, timeout).
  2. If Retry-After is present, wait that long. Otherwise use exponential backoff plus random jitter.
  3. When you retry a create, input, cancel, or upload request, send the same Idempotency-Key and the same body. If the request was already accepted, the same receipt comes back, so nothing runs twice.
  4. An error with retryable of false gives the same result if you resend it unchanged. Fix the request or the cause.
  5. If you resend a request whose response you never received under a new key, the same work can run twice. Do not change the key when retrying.
  6. Put a cap on the number of retries and the total wait.
def post_with_retry(url: str, key: str, body: dict, attempts: int = 5) -> httpx.Response: for attempt in range(attempts): try: response = httpx.post(url, headers={**AUTH, "Idempotency-Key": key}, json=body, timeout=30) except httpx.TransportError: response = None if response is not None and response.status_code < 400: return response try: error = response.json().get("error", {}) if response is not None else {} except ValueError: # a proxy may answer 502/503/504 without the JSON envelope error = {"retryable": response.status_code in (429, 502, 503, 504)} if response is not None and not error.get("retryable"): response.raise_for_status() delay = float(response.headers.get("Retry-After", 0)) if response is not None else 0 time.sleep(delay or min(30, 0.5 * 2 ** attempt)) raise RuntimeError("gave up after retries")

What each HTTP status means

StatusMeaning
200, 201Success. Reads and close are 200, file upload is 201
202Accepted. Read the result later.
400A request error such as a malformed cursor or correlation ID
401A key problem. Retrying a revoked key is pointless.
402An SU balance or budget problem
403Missing scope or permission
404The resource does not exist or is outside your access. Existence is not revealed.
409Conflict with the current state, for example already running or a key conflict
410The content is gone because its retention period passed
413, 415Size limit exceeded, unsupported format
422A value is outside the allowed range or schema
429Call rate or concurrent execution limit
500, 503A server side problem. Follow retryable and Retry-After.

Error code table

This table lists the public codes a caller can receive. A code with HTTP status - appears not as an HTTP response but only in error.code of a Turn or Run detail, and its retryable is false there. retryable describes resending the same request, so it is always false on a finished execution. Whether to create a new execution follows the “What to do after a failed execution” table below. A code used both as an HTTP response and in execution details, such as SESSION_SANDBOX_UNAVAILABLE, has one row: the status and retryable columns describe the HTTP response, and the meaning column also explains what it means in execution details. Codes used only internally are not listed. New codes may be added, so handle an unknown code by retryable and the HTTP status.

CodeHTTP statusretryableMeaning
NOT_FOUND404falseThe resource does not exist or is outside your access. For a Project that is not approved, every path returns this.
HTTP_ERROR4xxfalseA routing level rejection, such as a path or method that does not match.
INVALID_API_KEY401falseThe key is missing, malformed, revoked, or expired.
INSUFFICIENT_SCOPE403falseThe key does not have the scope for this operation.
ACTING_FORBIDDEN403falseDelegated access headers or parameters are not allowed.
WORKFLOW_DATA_DENIED403falseThe workflow tried to use data outside the data grant it received at publication.
ACTION_NOT_RESOLVABLE_BY_CALLER403falseThe key lacks explicit actions:approve delegation for this approval. For a work preparation scope confirmation, it lacks works:approve.
ACTION_CONTENT_CHANGED409falseReview content changed. Fetch it again and ask for a new human decision.
APPROVAL_CONTENT_UNAVAILABLE409falseReview content is unavailable or READ grants are insufficient.
APPROVAL_CONTENT_TOO_LARGE413falseReview content exceeds 100 rows or 256 KiB.
INVALID_INPUT400, 422falseThe request body or input format is invalid. Undeclared fields also land here.
LIMIT_OUT_OF_RANGE422falseA limits value is outside the allowed range. Values above the cap are not silently reduced.
INVALID_CURSOR400falseafter or Last-Event-ID is not a cursor of this resource, or they differ from each other.
INVALID_CORRELATION_ID400falseThe X-Correlation-Id format is invalid.
INVALID_IDEMPOTENCY_KEY422falseIdempotency-Key is missing or is not 1 to 256 bytes.
IDEMPOTENCY_CONFLICT409falseThe same Idempotency-Key was used with a different body or file. Use a new key for a new request.
INPUT_TOO_LARGE413falseThe JSON request body exceeds 64KiB.
FILE_TOO_LARGE413falseThe uploaded file exceeds 20MiB.
UNSUPPORTED_MEDIA_TYPE415falseThe body is not JSON, or it is compressed.
INVALID_FILENAME422falseThe filename is empty, exceeds 255 bytes, or contains a path separator (slash or backslash) or a control character. As an upload response, fix the name and upload again. In error.code of a Turn or Run detail, it means a result file the AI employee made during the execution had a name that breaks this rule, so the execution failed, and retryable is false there.
UNSUPPORTED_FILE_TYPE415falseThe filename extension and MIME type are not an allowed pair, or the content is not that format or is unsafe (for example a document with macros, or an encrypted PDF). It comes on upload, or when a Turn input includes a file type the Deployment does not accept. The allowed types are in limits and data. In error.code of a Turn or Run detail, it means a result file made during the execution had a type that is not allowed, so the execution failed, and retryable is false there.
OUTPUT_SCHEMA_MISMATCH422falseA value does not match the format or schema set at publication. As an HTTP response, the input of a Run creation request does not match the workflow’s input_schema. In error.code of a Turn or Run detail, it means a result did not match the output contract set at publication, so the execution failed, and retryable is false there. A schema mismatch in a JSON result is first handed back to the AI employee during the execution so it can fix it, and becomes this code only if the execution ends without a fix.
OUTPUT_LIMIT_EXCEEDED422falseA value exceeded its size or count limit. As an HTTP response, the input of a Run creation request exceeded a JSON limit (depth 16, 10,000 array elements in total). In error.code of a Turn or Run detail, it means a result exceeded its size or count limit, so the execution failed, and retryable is false there. It is closed as a failure, not silently truncated.
TOO_MANY_FILES422falseMore than 20 input files, or the same file was listed twice.
FILE_EXPIRED410falseThe input file is not ready or has expired. Upload it again.
ARTIFACT_EXPIRED410falseThe retention period has passed, so the result file can no longer be fetched. In the result list, that item shows availability: EXPIRED with this error.code.
PROJECT_STORAGE_LIMIT413falseThe Project file storage (500MiB) or the organization’s remaining capacity was exceeded.
RUN_STORAGE_LIMIT413falseThe file total of one execution (100MiB) was exceeded.
REVISION_CONFLICT409falseThe work or preparation changed after you read it. Read it again and send against the current state.
PREPARATION_IN_PROGRESS409falseA preparation for the same work is already in progress. Read the preparation in details.preparation_id.
INVALID_TRANSITION409falseThe preparation does not accept this request in its current state, for example because it ended or its confirmation deadline passed.
QUESTION_NOT_FOUND409falseThat preparation question is not open. Read the preparation again.
PACKAGE_CHANGED409falseThe setup the person reviewed is no longer the candidate. Show the new question and ask again.
INVALID_SPEC422falseThe work description breaks a rule. See details.reason.
INVALID_REQUEST422falseThe work preparation request breaks a rule. See details.reason.
SESSION_BUSY409falseThe Session already has a running Turn. Send after you confirm it ended.
SESSION_CLOSED409falseA closed Session does not accept input or files.
SESSION_EXPIRED410falseThe retention period of the Session content has ended.
SESSION_UPDATING503trueThe Session is being updated. Resend with the same Idempotency-Key.
SESSION_SANDBOX_UNAVAILABLE503trueThe Session execution environment could not be prepared right now. As an HTTP response, try again shortly. In error.code of a Turn or Run detail, it means the execution environment preparation step did not finish in time before work started, so the Turn failed, and retryable is false there. When this happens in a workflow step, the Run usually ends with this code too. If a request sent during preparation could not be confirmed, the Run stays EXTERNAL_EFFECT_UNKNOWN until usage is confirmed. Running out of time while waiting for an earlier execution is still EXECUTION_TIMEOUT. The request itself is fine, so create a new execution later.
DEPLOYMENT_UNAVAILABLE409falseThis Deployment cannot accept this operation now. Check its status.
WORKFLOW_UNAVAILABLE409falseThe workflow is not published, is stopped, or cannot be started through the API (for example a workflow that only starts from mail), so Run creation (POST /v1/workflows/{workflow_id}/runs) is refused. GET /v1/workflows/{workflow_id} reports this state not as an error response but as available: false and error.code in a 200 body. If the workflow is stopped during a Run, the Run ends CANCELED with this value in error.code of the Run detail. Try again after the publisher publishes or turns on the workflow again.
WORKFLOW_MANAGED_SESSION409falseThis is an internal Session used by a workflow. Use the Run paths.
RUN_TERMINAL409falseThe Run already has a settled outcome.
ACTION_EXPIRED409falseThe answer deadline of the question has passed.
ACTION_CANCELED409falseThe execution no longer waits for this question.
MINIMUM_SU_REQUIRED402falseFile operations need an account balance of at least 50 SU. 50 SU is not charged.
INSUFFICIENT_SU402falseThere is not enough available SU.
BUDGET_EXCEEDED402falseThe budget of this execution is exhausted or closed.
PROJECT_BUDGET_EXCEEDED402trueThe Project daily or monthly spend limit was exceeded. It works again after the limit window passes.
RATE_LIMITED429trueThe call rate limit was exceeded. Wait for Retry-After.
CONCURRENCY_LIMIT429trueThe Project concurrent execution limit was exceeded. Try again after an execution ends.
SOURCE_UNAVAILABLE503trueThe organization data store used to check the workflow input cannot be reached right now, so Run creation (POST /v1/workflows/{workflow_id}/runs) was refused. No Run was created. Resend later with the same Idempotency-Key. If it continues, tell the publisher.
BILLING_UNAVAILABLE503trueThe billing account could not be checked right now. Try again shortly.
ARTIFACT_NOT_READY409trueThe file is not ready yet. Try again shortly.
ARTIFACT_UNAVAILABLE503trueThe stored file cannot be read right now. Try again.
STORAGE_UNAVAILABLE503trueFile storage is unavailable right now. Try again.
EVENT_CURSOR_EXPIRED410falseThe event cursor has expired. Recover with the addresses in details.
OUTPUT_MANIFEST_UNAVAILABLE503trueThe result list cannot be read right now. Try again.
INTERNAL_ERROR500falseAn internal server error. Contact us with the request_id.
EXECUTION_TIMEOUT-falseThe Turn failed because it exceeded the execution time limit.
QUEUE_TIMEOUT-falseThe execution did not start within the queue time limit and expired.
RECOVERY_EXHAUSTED-falseThe server tried several recoveries but could not finish.
RUN_DEADLINE_EXCEEDED-falseThe whole Run deadline was reached first and the Run expired.
EXTERNAL_EFFECT_UNKNOWN-falseIt stopped because the outcome of a write to an external system could not be confirmed. Check the outcome yourself. It is not rerun automatically.
ACTION_REQUIRED_UNSUPPORTED-falseA human answer or approval became necessary, which the API does not support yet, so it failed.
CAPABILITY_DENIED-falseIt tried to use a tool or capability that was not allowed at publication.
SESSION_RESOURCES_NOT_READY-falseIt called a tool allowed by the publication that this stage does not yet support.
ARTIFACT_PREPARATION_TIMEOUT-falseResult file preparation exceeded its deadline.
ARTIFACT_PREPARATION_FAILED-falseA result file was not ready or had already expired when the execution ended, so it could not be returned.
OUTPUT_URL_INVALID-falseA URL result is not a public HTTPS address. Internal network addresses and addresses that carry login details, signatures, or tokens are refused too.
OUTPUT_CONTAINS_CREDENTIALS-falseA result (value, filename, file content, or URL) contains a credential pattern such as a key or password, so it was not returned.
OUTPUT_PATH_DENIED-falseIt tried to return a file from outside the Session attachments folder as a result.
OUTPUT_MISSING-falseA result marked required at publication was not produced.
EXECUTION_FAILED-falseThe execution failed because of an unexpected error.
CANCELED-falseThe execution stopped because of a cancel request.
AUTHORIZATION_REVOKED-falseThe execution was canceled because the key was revoked or expired, or its scope, the Deployment, or an account connection the Deployment uses became unusable.
SESSION_DELETED-falseThe Session was deleted, so its running Turn was canceled.
RUNTIME_CONTEXT_MISMATCH-falseThe execution failed because the Session was not in a state that can run it.
USAGE_PENDING-falseThe execution could not continue because earlier usage could not be confirmed as settled.

What to do after a failed execution

Once a Turn or Run ends as FAILED, CANCELED, or EXPIRED, that execution does not come back. To try again, create a new Turn or Run with a new Idempotency-Key. This differs from the retry above, which resends an unanswered request with the same key. Reusing the finished execution’s key returns that finished execution’s receipt again.

Follow the order below even when an AI tool makes the calls for you. A new execution costs money again.

DecisionCodesNext step
Once more, laterQUEUE_TIMEOUT, RECOVERY_EXHAUSTED, EXECUTION_FAILED, USAGE_PENDING, ARTIFACT_PREPARATION_TIMEOUT, ARTIFACT_PREPARATION_FAILED, SESSION_SANDBOX_UNAVAILABLEWait, then create one new execution with the same input. If it ends with the same code again, stop and contact us with the execution ID.
After changing conditionsEXECUTION_TIMEOUT, RUN_DEADLINE_EXCEEDED, BUDGET_EXCEEDED, INSUFFICIENT_SUShrink or split the input, and raise the deadline or budget within the range in limits and data or top up SU, then create a new execution. Do not resend right away under the same conditions.
Publishing settingsOUTPUT_SCHEMA_MISMATCH, OUTPUT_LIMIT_EXCEEDED, OUTPUT_MISSING, OUTPUT_PATH_DENIED, CAPABILITY_DENIED, SESSION_RESOURCES_NOT_READY, ACTION_REQUIRED_UNSUPPORTED, WORKFLOW_UNAVAILABLEThe caller often cannot fix these. Do not repeat the same input. Tell the person who published the Deployment or workflow. If your input made the result exceed a limit, shrink the input and create a new execution. For WORKFLOW_UNAVAILABLE, create a new Run only after the workflow is published or turned on again.
Check the result contentOUTPUT_URL_INVALID, OUTPUT_CONTAINS_CREDENTIALS, INVALID_FILENAME, UNSUPPORTED_FILE_TYPEA result the AI employee made broke a public result rule. Check that your input did not ask for an internal address, a credential, or a file type or name that is not allowed in the result, then create one new execution with the corrected input. If it ends with the same code again, tell the person who published the Deployment or workflow.
Check permissionsAUTHORIZATION_REVOKEDCheck the key, its scope, the Deployment status, and the publisher’s account connections. Create a new execution only after you receive a new key or permission or the connection is restored.
In a new SessionRUNTIME_CONTEXT_MISMATCHCreate a new Session and run there. If it repeats, contact us with the execution ID.
Do not run againEXTERNAL_EFFECT_UNKNOWNThe external system may already have been written to. Do not create a new execution until you check the result in that system yourself.
Intended endCANCELED, SESSION_DELETEDWhoever asked for the cancel or deletion decided this. Create a new execution only if that decision changes.

For a code not in this table, do not run again. Stop and tell a person.

When a result the AI employee produces during an execution breaks a public result rule, the execution ends as a failure right there. Among result format errors, only a schema mismatch in a JSON result is handed back to the AI employee during the execution to fix. In a workflow that returns only some results instead of failing (PARTIALLY_SUCCEEDED), the same code stays in error.code of the failed result item.

A failed organization work preparation is not an HTTP error either. Its reason is in the preparation’s reason_codes, and what to do next is in organization works.