Errors

Two shapes, decided by whether the response has started — and the codes in each.

Before the response begins: ProblemDetails

Anything caught before a run starts is a normal HTTP failure with an application/problem+json body. Every one carries status and instance, and all but the 429 and a 413 for a body whose declared length is over the limit carry a traceId (an oversize body sent in chunks is a 413 that does); some add a machine-readable code and fields of their own, listed below.

application/problem+json
{
  "type": "https://tools.ietf.org/html/rfc9110#section-15.5.1",
  "title": "Bad Request",
  "status": 400,
  "instance": "/v1/agents/amy/runs",
  "traceId": "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"
}

Status codes

  • 400 — the request is wrong, and the body says which field in errors: the key names the field and may be written in PascalCase, such as Message or EndUserRef. The message is empty or over 8,000 characters; a tool the agent does not have; an inline MCP server outside what the agent admits; an attachment id that is not yours or not stored; a decision scope the pause does not offer; an invalid userTimeZone or endUserRef. An upload with no file part, or a file Amy will not store, is a 400 too.
    400 application/problem+json
    {
      "title": "One or more validation errors occurred.",
      "status": 400,
      "errors": { "tools": ["Tools not available for agent 'amy': made_up_tool."] },
      "traceId": "00-4bf92f35…-01"
    }
  • 401 — the key is missing, malformed, unknown, revoked or expired. The response does not say which.
  • 403 — your plan does not allow the call. The body names the capability that refused in detail and says why in reason: limit_reached, not_in_plan, subscription_state or unknown_capability. limit and remaining are counts, or money in minor units when a currency is present. See Usage.
    403 application/problem+json
    {
      "type": "/errors/entitlement-required",
      "title": "Plan entitlement required.",
      "status": 403,
      "detail": "amy.tokens.month",
      "reason": "limit_reached",
      "limit": 100000,
      "remaining": 0
    }
    Deciding an approval that belongs to a team run is a 403 too, when the key may not decide it (team runs are a Console feature; if you do not use them, ignore this).
  • 404 — no such run, agent, file or conversation for you. Another organisation's resource, and another API client's run in your own organisation, is a 404 — never a 403.
  • 409 — the approval you are resolving is already decided or has expired, or the run cannot be retried (retry_refused, below).
  • 413 — the body is over the route's ceiling: 1 MB for JSON, 25 MB (plus a small allowance for the multipart envelope) for an upload. Check the size before you send. It is also the answer to a workspace file too large to stream back.
  • 415 — an upload that was not multipart/form-data. A different mistake from a file Amy will not store, which is a 400.
  • 422 — the request is well formed but Amy will not start the run: no provider key, a model without a price, or your organisation's privacy policy. Each carries a code, below.
    422 application/problem+json
    {
      "title": "No AI provider configured.",
      "status": 422,
      "code": "no_provider_credential",
      "traceId": "00-4bf92f35…-01"
    }
  • 429 — too many requests. The limit is counted per minute for your API client, which all the keys minted in one organisation share, and the response carries a Retry-After header in seconds. Wait that long. See Limits for the numbers.
  • 502 / 503 — a dependency we call was unavailable: storage, a document being read, a first-call set-up. Where the response has Retry-After, wait that long; otherwise back off.

Codes on a refusal

These arrive in the code member of the ProblemDetails body.

no_provider_credential

422. Amy found no AI provider key it may use for this key’s runs. API-key runs use the organisation’s keys, or Amy’s own where your organisation has been granted them; a team member’s personal key is never used. Nothing was started or billed.

An organisation Admin adds an Organization-scoped provider key and picks a default model in the Console under AI Providers. Do this before your first call.

model_unpriced

422. The model the run would use has no price on record, so its spend could not be metered or counted against the plan. The refusal carries provider and model.

Choose a model that has a price, or ask your administrator to add a rate for this one.

inference_policy_unsatisfied

422. Your organisation’s inference privacy policy does not allow this request, and nothing was sent to the provider. condition says which requirement failed: provider-unconstrainable, model-no-zdr-endpoint, evidence-stale or no-evidence.

An organisation Admin binds a provider and model the policy allows. The same value can arrive mid-run as a failure code.

retry_refused

409. The run cannot be retried. reason says why: not_failed, failure_not_retryable, retry_cap_reached, not_standalone, superseded, agent_unavailable or input_files_gone. The body also carries runStatus, retriesUsed and retryCap.

Branch on reason. not_failed: read the run, it has not failed. superseded: ask again in the conversation. agent_unavailable: reactivate the agent, then retry. input_files_gone: a file the run was given was discarded or deleted, so start a fresh run and send the file again. Any other reason: start a fresh run.

run_already_started

409, on /runs and /runs/await. A run was already started with this Idempotency-Key and the same request, so no second run started and nothing was billed again. The body carries runId and runStatus, and Location points at the run.

Read the run with GET /v1/runs/{runId} for its answer. Do not send the request again with a new key unless you want a second run.

idempotency_in_progress

409. The first request with this Idempotency-Key is still starting its run. Carries Retry-After: 1.

Wait the second, then send the same request again with the same key: it answers with the run once it exists.

idempotency_key_reused

409. This Idempotency-Key was already used with a different request: another route, another agent, or another body. Nothing was started.

Send a new key with a new request. Reuse a key only to repeat the request it was first sent with.

storage_unavailable

503. Amy could not fetch an attached file from storage before the run started. Carries a Retry-After header.

Wait the number of seconds in Retry-After, then send the same request again.

document_busy

503. Amy could not read an attached PDF right now because it is at capacity. Carries a Retry-After header.

Wait the number of seconds in Retry-After, then send the same request again.

seed_contention

503. Another request is setting up this organization’s defaults, which happens on its very first calls. Carries Retry-After: 1.

Wait the second, then send the same request again.

storage_not_configured

503. File storage is not set up on this deployment, so an upload cannot be accepted. It carries no Retry-After, because waiting does not fix it.

Do not retry in a loop: file upload is unavailable until the deployment’s storage is configured.

After it begins: an in-band error event

Once bytes are on the wire the status code is fixed at 200 and cannot be revised. So a run that fails mid-stream reports it as a frame, and the stream ends:

text/event-stream
event: error
data: {"code":"provider_unavailable","message":"The AI provider reported a fault on its own side, so the run could not finish. …","traceId":"4bf92f3577b34da6a3ce929d0e0e4736"}

This is the single most important thing to get right in a client: a 200 does not mean the run succeeded. Read the terminal frame. run_completed with status: "completed" is success; error is failure; anything else is a suspension or a policy stop, and its status says which.

The same code is recorded on the run. A run you submitted, or one you read after hanging up, shows it as errorCode, with the sentence failureReason and whether retryable is true. An awaited result carries the same errorCode and failureReason.

GET /v1/runs/{runId} (abridged)
{
  "id": "01936c2f-…",
  "status": "failed",
  "errorCode": "provider_rate_limited",
  "failureReason": "The AI provider refused the request because this account is over a rate or usage limit right now. …",
  "retriesUsed": 0,
  "retryCap": 3,
  "retryable": true
}

Why a run ends early or fails

Every value of code on an error frame and of errorCode on a run. "Retry" means POST /v1/runs/{runId}/retry, which re-drives a failed run from its own trace without running its tools again; each entry says whether Amy allows it. A run can be retried only as many times as its retryCap says.

run_timed_out

retry allowed

The AI model sent no output within its idle limit, so the run was timed out. This is not a limit on the run’s total length.

Retry the run, or start a new one.

tool_timed_out

retry refused

A tool or a delegated agent did not finish in time.

Start a new run. A retry is refused: the tool that never answered may already have done its work.

run_failed

retry allowed

The run failed before it could answer, for a reason none of the other codes names.

Retry the run, or start a new one.

provider_error

retry allowed

The AI provider answered with a failure that none of the provider codes below names.

Retry the run.

provider_quota_exceeded

retry allowed

The AI provider refused the call because the provider account is out of credit or over its plan limit.

An organisation Admin tops up or raises the limit with the provider; then retry. If the run used Amy-provided model access, that account is ours: tell us.

provider_budget_below_request

retry allowed

The provider key’s remaining spending limit could not cover the largest answer the request was allowed to produce.

Lower the agent’s maximum output tokens, or raise the key’s spending limit with the provider; then retry.

provider_tool_schema_rejected

retry refused

The AI model refused the run’s tool set: it does not accept how one of the tools is defined.

Bind a different model to the agent, or remove the tool, then start a new run.

provider_model_unavailable

retry allowed

The AI provider cannot serve a model the run asked for: the model does not exist, is not available to that account, or does not support something the run needs, such as tool use.

Check the agent’s model and the provider’s model bindings, then retry. A model you pinned with model on the request is sent again unchanged, so start a new run with another one instead.

provider_key_rejected

retry allowed

The AI provider refused the API key the run used: it is wrong, expired or revoked.

An organisation Admin replaces that provider’s key; then retry. If the run used Amy-provided model access, that key is ours: tell us.

provider_refused

retry refused

The AI provider declined the request, either because the key or account may not make it, or under its own content rules.

Check the provider account’s access to the model, or reword the request, and start a new run. A retry is refused because it would send the same content again.

provider_rate_limited

retry allowed

The AI provider says the account is over a rate or usage limit right now.

Wait a moment and retry; if it keeps happening, check the provider account’s limits.

provider_unavailable

retry allowed

The AI provider reported a fault on its own side.

Usually temporary: retry later.

provider_credential_missing

retry allowed

No API key is set up for the AI provider the agent’s model needs.

An organisation Admin adds one; then retry.

inference_policy_unsatisfied

retry refused

Your organisation’s privacy policy does not allow the model a step needed, so nothing was sent to the provider. It is the same value the 422 refusal at the start of a run carries.

An organisation Admin binds a model the policy allows. A retry is refused; start a new run after the change.

mcp_unavailable

retry refused

A connected MCP tool server could not be reached, so the run was stopped.

Fix the server, then start a new run. A retry is refused.

mcp_unauthorized

retry refused

A connected MCP tool server refused the credential the run sent, so the run was stopped.

Check the token you sent in mcpAuth for that server and the server’s registration, then start a new run.

agent_unavailable

retry refused

The run’s agent was deactivated or deleted before or while the run executed.

Start a new run on an agent that is active.

transaction_aborted

retry allowed

A database error on Amy’s side stopped the run before it could finish.

Retry the run; that is usually enough.

metering_unavailable

retry allowed

The run’s usage could not be recorded, so it was stopped before its next model call. Nothing further was sent to the model.

Retry the run; that is usually enough.

stale_run_swept

retry allowed

The run never reported a result and was closed by a clean-up sweep.

Retry the run.

worker_failed

retry allowed

The background worker running a submitted run failed before the run finished.

Retry the run.

quota_exceeded

retry refused

The monthly token allowance on Amy’s AI keys is used up. A run that used it up while going stops with whatever it had produced (status budget_exceeded); a submitted run that came up with nothing left, or a run that woke after waiting on background tasks, was stopped before any further model call. Tokens on your organisation’s own provider key never count.

Wait for the new month, or move to a plan with a larger allowance.

cost_budget_exhausted

retry refused

The organisation has spent its monthly Amy budget. The run stops with whatever it had produced (status budget_exceeded), or fails without a model call when it was still queued.

Wait for the budget to reset at the start of next month, or move to a plan with a larger one.

plan_inactive

retry refused

The organisation’s subscription is not active, or its plan does not include Amy runs, so the run was stopped before its next model call.

Fix the subscription on the billing page; then start a new run.

task_wait_timed_out

retry refused

The run was waiting for background tasks it started and they did not all finish in time. It ends with status expired and what it had.

Read the partial result, and start a new run if you need the rest.

task_wake_cap_reached

retry refused

The run reached its limit on how many times it may suspend and resume waiting for background tasks. It ends with status budget_exceeded and what it had.

Read the partial result, and start a new run if you need the rest.

task_deadline_exceeded

retry refused

A background task did not finish before its deadline and was stopped. The run that started it was told and carried on without the result.

Nothing to do on the task’s own run; read the parent run’s answer.

task_abandoned

retry refused

The run that started a background task finished before the task did, so the task was stopped.

Nothing to do; no run was left waiting for it.

output_truncated

retry refused

The answer was cut short because it reached the agent’s maximum output tokens. The run ends with status truncated and the partial answer.

Raise the agent’s maximum output tokens, then ask again.

run_budget_exceeded

retry refused

A background task used up its whole token budget before finishing, so it ended with what it had.

Start a new run with a narrower task, or split the work across smaller runs.

When a resume or a retry loses a race

Normally a resume or retry that cannot go ahead is refused before the stream opens, with the 409 above. If the state changes in the moment between that check and the stream, the answer arrives in-band instead:

approval_not_pending

The approval you resolved was already decided, or its run was no longer waiting, by the time the stream opened. Ordinarily the 409 before the stream catches this; this is the same answer for a decision that lost a race.

Read the run with GET /v1/runs/{runId} to see where it is.

run_not_resumable

The run is no longer awaiting approval.

Read the run with GET /v1/runs/{runId}.

run_not_retryable

The run is not a failed run, has no committed trace to retry from, or is no longer retryable.

Read the run, and start a fresh run if you still need the answer.

retry_cap_reached

The run has been retried as often as it may be.

Start a fresh run.

inline_mcp_mismatch

The inline MCP servers you re-declared on a resume or retry do not match the ones the run started with. A swap is refused.

Send the same mcpServers you started the run with.

Trace ids

Both shapes carry a traceId, and it names the same trace, but not in the same form. A ProblemDetails body gives the full W3C identifier, 00-<32 hex>-<16 hex>-01; a stream's error frame and the awaited result of a failed run give only the 32-hex trace id, which is the second segment of the full one (an awaited result of a run that did not fail has none). To match the two, compare that segment. Log it next to your own request id.

Retrying

  • Safe to retry the same request: a 429 (after Retry-After; see Limits), a 502, a 503 other than storage_not_configured, and a network failure before the response started. A start call is safe to retry only with the same Idempotency-Key it was first sent with. Back off — do not hammer.
  • Send a start call with an Idempotency-Key, and it is safe to retry. The same key and request again starts no second run and bills nothing twice: a repeated submit answers its first 202, a repeated stream or await answers 409 run_already_started with the runId to read with GET /v1/runs/{runId}, and a first request still starting answers 409 idempotency_in_progress — wait Retry-After and send it again (Limits, Idempotency keys). Without a key, the same start call sent twice starts two runs and bills you for both.
  • A failed run is retried with the retry call, not by starting again. It re-drives the run from what it already paid for, and refuses with a 409 retry_refused whenever that would be wrong. Check retryable on the run before offering it.
  • Cancel is safe to repeat. Cancelling a run that already ended is a 200 that does nothing.
  • Do not retry a 4xx unchanged, with these exceptions: a 429 clears after Retry-After, and a 422 or 403 clears once someone fixes what it names — add the provider key, pick a priced model, change the plan — after which the same request works. Any other 4xx fails identically.