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.
{
"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 asMessageorEndUserRef. 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; adecisionscope the pause does not offer; an invaliduserTimeZoneorendUserRef. An upload with nofilepart, 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
detailand says why inreason:limit_reached,not_in_plan,subscription_stateorunknown_capability.limitandremainingare counts, or money in minor units when acurrencyis present. See Usage.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).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 } - 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-Afterheader 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.
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:
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.
{
"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 allowedThe 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 refusedA 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 allowedThe 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 allowedThe AI provider answered with a failure that none of the provider codes below names.
Retry the run.
provider_quota_exceeded
retry allowedThe 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 allowedThe 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 refusedThe 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_key_rejected
retry allowedThe 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 refusedThe 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 allowedThe 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_credential_missing
retry allowedNo 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 refusedYour 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_unauthorized
retry refusedA 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.
transaction_aborted
retry allowedA database error on Amy’s side stopped the run before it could finish.
Retry the run; that is usually enough.
stale_run_swept
retry allowedThe run never reported a result and was closed by a clean-up sweep.
Retry the run.
worker_failed
retry allowedThe background worker running a submitted run failed before the run finished.
Retry the run.
quota_exceeded
retry refusedThe 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 refusedThe 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 refusedThe 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 refusedThe 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 refusedThe 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 refusedA 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 refusedThe 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 refusedThe 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 refusedA 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 thanstorage_not_configured, and a network failure before the response started. A start call is safe to retry only with the sameIdempotency-Keyit 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 first202, a repeated stream or await answers409 run_already_startedwith therunIdto read withGET /v1/runs/{runId}, and a first request still starting answers409 idempotency_in_progress— waitRetry-Afterand 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_refusedwhenever that would be wrong. Checkretryableon 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.