Model APIs

Model errors

On this page
  1. The problem object
  2. Guidance
  3. Error codes
    1. out_of_credits
    2. rate_limit
    3. prompt_nsfw
    4. ip_detected
    5. job_failed
    6. timeout
    7. validation
    8. confirmation_rejected
    9. canceled
    10. job_not_cancellable
    11. approval_required
    12. run_ended
  4. Agent error codes
  5. Duplicate submissions
  6. Retrying safely

A refused submission returns an HTTP problem. An accepted generation can fail later, in which case the job remains readable and explains the failure. Read the machine-readable code to choose an action and the recorded refund outcome to tell the customer what happened to their credits.

Errors
Errors

The problem object #

The API uses the RFC 7807 Error object. On a submit refusal, code is a field of that problem body; on an accepted job, the typed code is failure.code. An HTTP 200 from GET /jobs/{id} can contain a job whose status is failed.

Field Type Required Description
code string No Machine-readable error code.…
type string Yes A URI reference identifying the problem type.
title string Yes
status integer Yes
detail string No
instance string No
job_id string No The job this refusal points at, so a client can follow it without reading detail.…
request_id string No

This is the production 400 response for an unknown image model, captured on 2026-09-21.

JSON400 Bad Request — production response
{
  "code": "validation",
  "detail": "unknown image model",
  "request_id": "localhost/7bnUUXx7b2-007287",
  "status": 400,
  "title": "Bad Request",
  "type": "about:blank"
}
Field in this response Meaning
code validation: change the request before submitting again.
detail Human-readable explanation; do not parse it for application logic.
request_id Identifier to include with a support report.
status The HTTP refusal status, 400.
title, type The problem's short title and type URI.

Guidance #

Branch on code, never on detail or failure.message. Show the explanation to the customer and retain request_id for support. Treat an unfamiliar generation code as job_failed; if no code is supplied, use the status and structured fields such as job_id.

For a failed job, failure.credits_refunded: true means the hold was released and the job cost nothing. false means charged. An absent or null value does not prove either outcome: the hold may still be settling, no hold may exist, or the job may predate the field.

Field Type Required Description
code GenerationErrorCode No Stable refinement of kind, present on every job that failed after this field shipped and derived from the recorded reason for older ones.…
kind string Yes What stopped the job.…
message string Yes Human-readable reason, safe to show the customer as-is. The same text as error.detail.
credits_refunded boolean, nullable No What the credit ledger actually did with this job's credit hold, recorded when the hold settled.…

There is no separate runner-error taxonomy or error-type response header; use the problem status and code, or the job's typed failure.

Error codes #

These eleven values come from the generation contract. The sections below distinguish an HTTP refusal from a code on a failed job; an upstream status inside job.error is not the status of your successful job read.

Value Meaning
out_of_credits the wallet cannot pay for the job. Nothing was submitted and nothing was charged. Top up, or submit a cheaper model or fewer seconds.
rate_limit refused for now, not refused outright - the caller's own concurrency ceiling, or the provider's. Retry later; the same request will be accepted.
prompt_nsfw a content filter refused the request or the result it produced, on safety grounds. Editing the prompt or the reference media is the fix. Whether the credits were refunded is failure.credits_refunded, not this code.
ip_detected a content filter refused it for a real person's likeness or a protected work, rather than for safety. The fix is different from prompt_nsfw - change the reference image or the named subject, not the tone of the prompt - which is why it is its own code.
job_failed the job ran and did not produce an asset for any other reason, including a provider error. The default for an unclassified failure.
timeout the job ran past its time budget, or a GET /jobs/{id}/wait returned before the job reached a terminal status. On a failed job the work is over; on a wait the job may still be running.
validation the request itself is not acceptable - a missing or malformed field, a model that does not support the capability asked of it, a duration the model will not render. Nothing was submitted and nothing was charged. Retrying unchanged will fail identically.
confirmation_rejected the cost confirmation gate did not pass. Either a client showed the customer a quote and the customer declined, or a supplied confirmation token was refused. A submit that carries no confirmation token is never refused for this reason.
canceled the job was canceled by its owner (POST /jobs/{id}/cancel), not broken. It is the code the SDK wait helpers raise for a canceled job; cancellation on the job says what the provider did and what was refunded. A canceled job carries no failure.
job_not_cancellable POST /jobs/{id}/cancel refused (409) because the job already finished, or its finished result is already being delivered. The job will reach succeeded or failed on its own; nothing was changed.
approval_required refused with 402 (title Approval Needed) because the generation would take an agent run past the price its customer approved (the credit_ceiling a guided preset's review card showed, sent with the brief). Nothing was submitted and nothing was charged. The detail names the generation's cost and the new run total; the agent must ask the customer to approve that total before it continues, never retry, split the job or switch models to fit.
run_ended refused with 409 (title Run Ended) because the agent run this generation was started for (the turn its turn-scoped credential names) has already ended: the platform failed, swept or stopped the turn, or it finished and delivered its reply. Nothing was submitted and nothing was charged. The agent must stop working on that run, never retry or switch models; the customer's chat already shows how it ended.

out_of_credits #

The available wallet balance or organization member budget cannot cover the generation. The server refuses the request before starting billable work.

Property Value
HTTP status 402 Payment Required; 402 Budget Exceeded for an organization member budget.
Refund No refund needed: nothing submitted and nothing charged.
Retry unchanged? Only after adding credits or restoring budget.
What to do Check the spendable balance, top up, or quote a cheaper model or shorter duration.
JSON402 — example from the Error schema
{
  "type": "about:blank",
  "title": "Payment Required",
  "status": 402,
  "detail": "The available credits cannot cover this generation.",
  "code": "out_of_credits"
}
Field Meaning
type, title, status Problem metadata; the request was refused with 402.
code out_of_credits distinguishes the wallet refusal from another payment requirement.
detail Explanation for the customer; this schema-built example is not a production capture.

rate_limit #

The caller has reached a generation concurrency ceiling or quota, or capacity is temporarily unavailable. A refused submit has no new job; an already accepted job waiting on provider capacity keeps its existing id.

Property Value
HTTP status 429 Too Many Requests.
Refund No refund needed for a local concurrency or quota refusal: no new hold.
Retry unchanged? Yes, after capacity becomes available or the quota resets.
What to do Honour Retry-After when supplied and Retry-After-Reset on quota refusals; otherwise use backoff.

The example uses the handler's detail for two active generations on a two-slot plan.

JSON429 — example from the Error schema
{
  "type": "about:blank",
  "title": "Generation Concurrency Limit Reached",
  "status": 429,
  "detail": "You have 2 generations running, which is the concurrent maximum of 2 for your plan. Wait for one to finish or upgrade your plan to run more at once.",
  "code": "rate_limit"
}
Field Meaning
type, title, status Problem metadata; the submit was refused with 429.
code rate_limit signals a temporary refusal.
detail Active count and ceiling in this example. Read live counts from GET /me, not by parsing this sentence.

See Concurrency limits for the limits and Platform headers for reset timing.

prompt_nsfw #

A provider's safety filter refused the input or blocked the result it generated. The name does not mean the prompt alone caused it: reference media and output moderation can also trigger this code.

Property Value
Where it appears failure.code on a failed job; also an HTTP problem for an immediate refusal.
HTTP status 400 for a remembered policy refusal repeated locally; 422 when an immediate upstream content-policy rejection reaches the submit error handler.
Refund Yes, unless the provider billed the refused attempt. A refusal whose provider answer shows no billable work, or says nothing either way, is refunded. GPT Image refusals are always refunded, including a block of the generated image, because OpenAI bills only the images it delivers. Read failure.credits_refunded. A local repeat refusal costs nothing.
Retry unchanged? No automatic retry; change the relevant prompt or media.
What to do Check whether the explanation identifies the input or generated output, then revise that part.
JSONFailed-job excerpt — example from JobFailure, refunded input refusal
{
  "status": "failed",
  "failure": {
    "kind": "moderated",
    "code": "prompt_nsfw",
    "message": "Your request was rejected by the provider's safety system. Your credits were not charged: the provider refused this request before it did any billable work. Edit your prompt or reference image and try again.",
    "credits_refunded": true
  }
}
Field Meaning
status The job is terminal; this excerpt is not a complete Job.
failure.kind, failure.code A moderated job, narrowed to a safety refusal.
failure.message Customer-facing explanation from the refunded refusal path.
failure.credits_refunded true confirms a release in this example; do not infer it from the code.

ip_detected #

A content filter identified a recognizable person's likeness or a protected work. Changing a safety-related word in the prompt may not fix a refusal of the reference image or named subject.

Property Value
Where it appears failure.code on a failed job; also an HTTP problem for an immediate refusal.
HTTP status 400 for a remembered likeness refusal repeated locally; 422 when an immediate upstream likeness rejection reaches the submit error handler.
Refund Yes, unless the provider billed the refused attempt. A likeness refusal the model provider raises on the finished output is always refunded. failure.credits_refunded is authoritative.
Retry unchanged? No automatic retry.
What to do Change the reference image or named subject and follow the provider-specific explanation.
JSONFailed-job excerpt — example from JobFailure, refunded likeness refusal
{
  "status": "failed",
  "failure": {
    "kind": "moderated",
    "code": "ip_detected",
    "message": "The reference was refused because it looks like a recognizable real person or a protected work.",
    "credits_refunded": true
  }
}
Field Meaning
status Terminal failure; this is a schema-built excerpt, not a captured job.
failure.kind, failure.code A moderation failure specifically about likeness or protected work.
failure.message The explanation to show; the example is shortened.
failure.credits_refunded true is the refund outcome illustrated here.

job_failed #

The generation did not produce an asset for an operational or otherwise unclassified reason. This is also the fallback for an unfamiliar generation error code.

Property Value
Where it appears failure.code on the failed job, or a submit problem when submission itself fails.
HTTP status Submit handler: 502 for an upstream failure, 500 for an internal failure; 422 for an upstream client or provider-billing rejection without a more specific code.
Job read GET /jobs/{id} can still return 200; the job's error.status describes its failure, commonly 502.
Refund Yes for an operationally failed job. Confirm the settled outcome in failure.credits_refunded.
Retry unchanged? With backoff for a temporary operational failure; first check any existing job. A 422 needs its explanation addressed.
What to do Keep the job id and request id, read the failure, and submit a new attempt only when the previous job is terminal.

The message and refund below match the observed Gemini video failure used in the Jobs guide. No complete failed-job production fixture was saved; this is a schema-built excerpt.

JSONFailed-job excerpt — example from JobFailure
{
  "status": "failed",
  "failure": {
    "kind": "error",
    "code": "job_failed",
    "message": "Video generation failed due to an internal server issue…",
    "credits_refunded": true
  }
}
Field Meaning
status Terminal job failure.
failure.kind, failure.code An operational or unclassified failure.
failure.message Provider failure explanation, shortened here.
failure.credits_refunded The credit hold was released.

timeout #

Distinguish a closed HTTP wait window from an exhausted generation deadline. Only the latter ends the job.

Property Value
HTTP status 408 Request Timeout from GET /jobs/{id}/wait.
On the job failure.code: timeout means the generation reached a terminal deadline.
Refund A wait request never charges and does not settle the generation hold. A terminal generation timeout refunds it.
Retry unchanged? Repeat the wait on the same job id. For a terminal timeout, decide whether to start a new generation.
What to do Read the job's status before treating the generation as failed.
JSON408 — example from the Error schema
{
  "type": "about:blank",
  "title": "Request Timeout",
  "status": 408,
  "detail": "job did not finish before timeout",
  "code": "timeout"
}
Field Meaning
type, title, status This HTTP wait ended with 408.
code timeout describes the wait request in this example.
detail The job did not finish inside that wait window; it may still be running.

validation #

A field is missing or malformed, a model is unknown, or the chosen model does not support a requested duration, capability or field combination.

Property Value
HTTP status 400 Bad Request.
Refund No refund needed: nothing submitted and nothing charged.
Retry unchanged? No, except the observed new-model rollout case below.
What to do Compare the request to GET /models and Common model arguments; correct the offending fields.
JSON400 — production validation response
{
  "code": "validation",
  "detail": "unknown image model",
  "request_id": "localhost/7bnUUXx7b2-007287",
  "status": 400,
  "title": "Bad Request",
  "type": "about:blank"
}
Field Meaning
code, status A 400 validation refusal.
detail The image model was not recognized.
request_id Identifier for support correlation.
title, type Standard problem metadata.

confirmation_rejected #

The optional cost confirmation did not match the request being submitted: it expired, was invalid, belonged to another account, or the body, model or price changed. The enum also covers a client declining a quote. A submit without a confirmation token is never refused for this reason.

Property Value
HTTP status 422 Unprocessable Entity.
Refund No refund needed: the confirmation gate runs before the credit reservation.
Retry unchanged? No; obtain a fresh quote first.
What to do Call POST /jobs/cost, show the new price, and submit that exact body with its new token before expires_at.
JSON422 — example from the Error schema
{
  "type": "about:blank",
  "title": "Unprocessable Entity",
  "status": 422,
  "detail": "this quote has expired — ask for a fresh price and try again",
  "code": "confirmation_rejected"
}
Field Meaning
type, title, status The confirmation gate refused the submit with 422.
code confirmation_rejected means re-quote.
detail The handler's explanation for an expired quote.

canceled #

The job's owner canceled it with POST /jobs/{id}/cancel. It is not a failure: the job carries status: canceled and a cancellation object, never a failure. The SDK wait helpers raise this code for a canceled job.

Property Value
HTTP status None: it is a job state. The cancel call itself answers 200.
On the job status: canceled, with cancellation.stage, provider_cancel, settlement and message.
Refund cancellation.settlement: refunded before the job reached the provider or when the provider stopped it without billing, partially_refunded when the provider bills only the part it rendered, charged when it finished and billed, pending until the provider answers.
Retry unchanged? Submit a new job if you still want the result; a canceled job is never delivered.
What to do Show cancellation.message as written, and read the job again while settlement is pending.

job_not_cancellable #

POST /jobs/{id}/cancel refused because the job already finished, or the provider already finished it and the result is being delivered.

Property Value
HTTP status 409 Conflict.
Refund Nothing changed: the job settles under the usual rules when it reaches succeeded or failed.
Retry unchanged? No; the job will not become cancelable.
What to do Read the job; it reaches its own terminal status shortly.
JSON409 — example from the Error schema
{
  "type": "about:blank",
  "title": "Conflict",
  "status": 409,
  "detail": "This job already finished, so there is nothing to cancel.",
  "code": "job_not_cancellable"
}
Field Meaning
type, title, status The cancel was refused with 409.
code job_not_cancellable means the job is finished or finishing.
detail Which of the two applies.

approval_required #

An agent run started from a guided preset carries the price its review card showed ("Up to N", sent as credit_ceiling with the brief). A generation that would take the run past that price is refused before anything is submitted, so the agent stops and asks the customer to approve the new total instead of spending it.

Property Value
HTTP status 402 Approval Needed.
Refund No refund needed: nothing submitted and nothing charged.
Retry unchanged? No. Ask the customer to approve the new total; do not retry, split the job or switch to a cheaper model to fit.
What to do Tell the customer what the next generation makes, what it costs and the run's new total, and continue once they approve.
JSON402 — example from the Error schema
{
  "type": "about:blank",
  "title": "Approval Needed",
  "status": 402,
  "detail": "This generation costs 65 credits and would bring this run to 163 credits, above the 147 the customer approved. Nothing was charged. Stop and ask the customer to approve 163 credits for this run before you continue; do not retry, split the job or switch to a cheaper model to fit.",
  "code": "approval_required"
}
Field Meaning
type, title, status The generation was refused with 402; the account can pay, the run's approved price cannot.
code approval_required distinguishes the run price from an empty wallet (out_of_credits) and a plan limit.
detail The generation's cost, the run's new total and the approved price.

run_ended #

A generation submitted with an agent run's turn-scoped credential after the platform has already settled that run: the turn failed, the platform swept it, the customer stopped it, or it finished and its reply was delivered. The run's pod can keep working for a while after that, and anything it started now would be charged for work nobody receives as the run's reply, so it is refused before anything is submitted.

Property Value
HTTP status 409 Run Ended.
Refund No refund needed: nothing submitted and nothing charged.
Retry unchanged? No. The run is over; retrying, switching models or splitting the job is refused the same way.
What to do Stop working on that run. The customer's chat already shows how it ended, and anything the run finished is in their Library; they continue by sending a new message.
JSON409 — example from the Error schema
{
  "type": "about:blank",
  "title": "Run Ended",
  "status": 409,
  "detail": "This agent run already ended, so this generation was refused. Nothing was charged. Stop working on this run: do not retry, switch models or start new generations for it. The customer's chat already shows how the run ended; they can send a new message to continue.",
  "code": "run_ended"
}
Field Meaning
type, title, status The generation was refused with 409; the account can pay, but the run it belongs to is over.
code run_ended distinguishes an ended run from the run's price (approval_required) and an empty wallet (out_of_credits).
detail Whether the run ended or finished, and what the agent does next.

Agent error codes #

Agent endpoints and failed turns use a separate enum. See Agent Sessions API for the session and turn flow.

Value Meaning
session_busy the session already has a turn in flight or too many turns are pending; wait or steer.
access_denied the credential or organization role may not do this.
insufficient_credits the wallet cannot pay for the turn.
backend_error the agent could not be reached or failed.
timeout the turn ran past its time budget.

Duplicate submissions #

The same generation body and Idempotency-Key, or the same body with no key both times, is refused within five minutes with 409 and the existing job_id. It carries no code: the status and job identifier are the contract. That refusal is never billed.

JSON409 Conflict — production response
{
  "detail": "this exact request was already submitted as job 35b6ff8d-0c78-440b-bdb3-bfe476b56d76 less than 5m0s ago and has not been billed twice — check it with GET /jobs/35b6ff8d-0c78-440b-bdb3-bfe476b56d76. To run it again anyway, resubmit with a different Idempotency-Key header.",
  "job_id": "35b6ff8d-0c78-440b-bdb3-bfe476b56d76",
  "request_id": "localhost/7bnUUXx7b2-007291",
  "status": 409,
  "title": "Conflict",
  "type": "about:blank"
}
Field Type Required Description
type string Yes A URI reference identifying the problem type.
title string Yes
status integer Yes
detail string No
job_id string No The job this refusal points at, so a client can follow it without reading detail.…
request_id string No

Follow job_id with a job read or wait. Keep the same key on retries; use a distinct key only for a deliberate new generation of identical input. Sets have separate per-member behavior; see Sets.

Retrying safely #

Retry 408 waits on the existing job. Retry temporary 429, 502 and 503 refusals with bounded exponential backoff and jitter, keeping the same Idempotency-Key on a generation submit. Honour any Retry-After delay and Retry-After-Reset time. A lost response may have accepted the work, so a resulting 409 is your route back to that job.

Fix authentication, balance, access or validation problems before retrying. For a failed job, inspect failure.code, failure.message and failure.credits_refunded before creating another attempt. Request errors covers the complete HTTP status table.