Bản lưu ngày / Snapshot of 2026-10-18 · Xem bản mới nhất / See the latest version
WanduraDocsBeta
BetaThese docs are in beta and change often. Something wrong, unclear or missing? Tell us.
Change history

History: Errors

Every change to this page, newest first. Expand an entry to see what changed.

← Errors

  1. 16 Oct 2026 · +173 −27 lines
    Show changes
      # Errors
      
    - > **TL;DR** — Branch on `error.code`, not the message. Retry only the codes marked Yes, and network errors, with the same `Idempotency-Key`.
    + > **TL;DR** — Branch on `error.code`, not the message. Retry only the codes marked Yes, and network errors, with the same `Idempotency-Key`. Each error links to its own section on this page.
      
      Errors are JSON: `{"error": {"code": "...", "message": "..."}}`. Branch on `code`; the message is for people and may change. Every response has an `X-Request-Id` header: quote it to support.
    ⋯
      Retry only the codes marked **Yes** below and network errors, with the same `Idempotency-Key`. Wait for `Retry-After` when the response has it, otherwise back off with jitter. Never retry the others: the same request will fail the same way.
      
    - | HTTP | `code` | Retry? | What to do |
    - |---|---|---|---|
    - | 400 | `invalid_request` | No | The body or a parameter is wrong; the message says which. |
    - | 400 | `idempotency_key_required` | No | Send an Idempotency-Key header (max 255 chars) on every submit. |
    - | 400 | `end_user_id_required` | No | end_user_id is required: the id of the person in your app this run is for. |
    - | 401 | `invalid_api_key` | No | Missing, unknown or revoked API key. |
    - | 401 | `api_key_expired` | No | This API key has expired. Create or rotate a key. |
    - | 402 | `wallet_empty` | No | The product wallet and the org pool cannot cover this run. Add money, then retry. |
    - | 403 | `insufficient_scope` | No | Rolling out. This key has the Read only permission: it can read runs and styles, not submit, cancel, share or upload. Use a key with the Run styles permission. |
    - | 403 | `style_not_allowed` | No | Rolling out. This key may run only some styles, and this is not one of them. Use a key that lists it, or ask your org admin. |
    - | 404 | `style_not_found` | No | No style with that id for your org (or not public). |
    - | 404 | `run_not_found` | No | No run with that id for this product. |
    - | 404 | `not_found` | No | No such endpoint. |
    - | 409 | `idempotency_key_reused` | No | This Idempotency-Key was already used with a different body. Use a new key for a new run. |
    - | 409 | `run_not_cancelable` | No | Only queued runs can be canceled. |
    - | 413 | `file_too_large` | No | The file is over the size limit for its type. |
    - | 415 | `unsupported_content_type` | No | Uploads accept JPEG, PNG, WEBP, MP4, MOV and WEBM. |
    - | 429 | `end_user_cap_reached` | Yes | This end user reached the daily run cap. Retry after the Retry-After header (resets 00:00 UTC). |
    - | 429 | `gateway_rate_limited` | Yes | A gateway key's product reached its runs-per-minute limit. Retry after the Retry-After header. |
    - | 429 | `uploads_rate_limited` | Yes | Your org reached its presigned uploads per minute. Retry after the Retry-After header. |
    - | 429 | `product_busy` | Yes | Your product's concurrency slots and its queue are full. Retry after the Retry-After header (about 5 s). |
    - | 500 | `internal_error` | Yes | Something went wrong on our side. Retry with the same Idempotency-Key; quote X-Request-Id to support. |
    - | 503 | `style_unavailable` | Yes | This style cannot run on a live key right now (no model behind it). Test keys still return a sample. |
    - | 503 | `uploads_unavailable` | Yes | Uploads are not available on this deployment right now. |
    - | 503 | `service_unavailable` | Yes | The job queue is briefly unavailable; nothing was created or charged. Retry with the same Idempotency-Key after the Retry-After header. |
    + ## Error body and headers {#error-body}
      
    + **Rolling out.** Every `/v2` error answer, in both shapes (runs and [Jobs API](#jobs-errors)), gains these, on top of every field it already has:
    + 
    + | Where | Name | What it says |
    + |---|---|---|
    + | Body | `error.docs_url` | A link to this code's section on this page, for example `https://docs.wandura.ai/developers/api-v2/errors#product-busy`. A code without its own section links to this page. |
    + | Body | `error.retryable` | `true` when the same request can succeed later unchanged: every `429` and `5xx` (for example `product_busy`, `service_unavailable`). `false` for input and auth errors. Jobs API errors already have it. |
    + | Header | `Wandura-Error-Code` | The same value as `error.code`. |
    + | Header | `Wandura-Should-Retry` | `true` or `false`, the same value as `error.retryable`. |
    + 
    + `Retry-After` and `X-Request-Id` stay as they are. All four are optional: ignore them if you do not use them, and treat a missing `retryable` like the table below.
    + 
    + ```http
    + HTTP/1.1 429
    + Retry-After: 5
    + Wandura-Error-Code: product_busy
    + Wandura-Should-Retry: true
    + 
    + {"error":{"code":"product_busy","message":"…","limit":20,"retry_after_s":5,"retryable":true,"docs_url":"https://docs.wandura.ai/developers/api-v2/errors#product-busy"}}
    + ```
    + 
    + ## Run errors {#run-errors}
    + 
    + | HTTP | `code` | Retry? |
    + |---|---|---|
    + | 400 | [`invalid_request`](#invalid-request) | No |
    + | 400 | [`idempotency_key_required`](#idempotency-key-required) | No |
    + | 400 | [`end_user_id_required`](#end-user-id-required) | No |
    + | 401 | [`invalid_api_key`](#invalid-api-key) | No |
    + | 401 | [`api_key_expired`](#api-key-expired) | No |
    + | 402 | [`wallet_empty`](#wallet-empty) | No |
    + | 403 | [`insufficient_scope`](#insufficient-scope) | No |
    + | 403 | [`style_not_allowed`](#style-not-allowed) | No |
    + | 404 | [`style_not_found`](#style-not-found) | No |
    + | 404 | [`run_not_found`](#run-not-found) | No |
    + | 404 | [`not_found`](#not-found) | No |
    + | 409 | [`idempotency_key_reused`](#idempotency-key-reused) | No |
    + | 409 | [`run_not_cancelable`](#run-not-cancelable) | No |
    + | 413 | [`file_too_large`](#file-too-large) | No |
    + | 415 | [`unsupported_content_type`](#unsupported-content-type) | No |
    + | 429 | [`end_user_cap_reached`](#end-user-cap-reached) | Yes |
    + | 429 | [`gateway_rate_limited`](#gateway-rate-limited) | Yes |
    + | 429 | [`uploads_rate_limited`](#uploads-rate-limited) | Yes |
    + | 429 | [`product_busy`](#product-busy) | Yes |
    + | 500 | [`internal_error`](#internal-error) | Yes |
    + | 503 | [`style_unavailable`](#style-unavailable) | Yes |
    + | 503 | [`uploads_unavailable`](#uploads-unavailable) | Yes |
    + | 503 | [`service_unavailable`](#service-unavailable) | Yes |
    + 
      Submit answers, `429` ones included, carry `X-Concurrency-Limit`, `X-Concurrency-Running` and `X-Concurrency-Waiting`. Each `product_busy` and `gateway_rate_limited` answer is a refusal: it is listed at `GET /v2/queue/refusals`. See [Queue and congestion](queue-and-congestion.md).
      
      A run that fails after it was accepted is not an HTTP error: the run ends `failed` or `nsfw` with a [free-run reason](concepts.md#free-runs), and you are not charged.
      
    + #### `invalid_request` {#invalid-request}
    + `400`, do not retry. The body or a parameter is wrong; the message says which.
    + 
    + #### `idempotency_key_required` {#idempotency-key-required}
    + `400`, do not retry. Send an `Idempotency-Key` header (max 255 chars) on every submit.
    + 
    + #### `end_user_id_required` {#end-user-id-required}
    + `400`, do not retry. `end_user_id` is required: the id of the person in your app this run is for.
    + 
    + #### `invalid_api_key` {#invalid-api-key}
    + `401`, do not retry. Missing, unknown or revoked API key.
    + 
    + #### `api_key_expired` {#api-key-expired}
    + `401`, do not retry. This API key has expired. Create or rotate a key.
    + 
    + #### `wallet_empty` {#wallet-empty}
    + `402`, do not retry. The product wallet and the org pool cannot cover this run. Add money, then retry.
    + 
    + #### `insufficient_scope` {#insufficient-scope}
    + `403`, do not retry. **Rolling out.** This key has the Read only permission: it can read runs and styles, not submit, cancel, share or upload. Use a key with the Run styles permission. 
    + #### `style_not_allowed` {#style-not-allowed}
    + `403`, do not retry. **Rolling out.** This key may run only some styles, and this is not one of them. Use a key that lists it, or ask your org admin.
    + 
      A `403` only comes from a key made with a permission or a list of allowed styles (see [API keys](concepts.md#api-keys)). A key made without them never gets one.
      
    - The [Jobs API](jobs.md#errors) uses its own error codes.
    + #### `style_not_found` {#style-not-found}
    + `404`, do not retry. No style with that id for your org (or not public).
    + 
    + #### `run_not_found` {#run-not-found}
    + `404`, do not retry. No run with that id for this product.
    + 
    + #### `not_found` {#not-found}
    + `404`, do not retry. No such endpoint.
    + 
    + #### `idempotency_key_reused` {#idempotency-key-reused}
    + `409`, do not retry. This `Idempotency-Key` was already used with a different body. Use a new key for a new run.
    + 
    + #### `run_not_cancelable` {#run-not-cancelable}
    + `409`, do not retry. Only queued runs can be canceled.
    + 
    + #### `file_too_large` {#file-too-large}
    + `413`, do not retry. The file is over the size limit for its type.
    + 
    + #### `unsupported_content_type` {#unsupported-content-type}
    + `415`, do not retry. Uploads accept JPEG, PNG, WEBP, MP4, MOV and WEBM.
    + 
    + #### `end_user_cap_reached` {#end-user-cap-reached}
    + `429`, retry. This end user reached the daily run cap. Retry after the `Retry-After` header (resets 00:00 UTC).
    + 
    + #### `gateway_rate_limited` {#gateway-rate-limited}
    + `429`, retry. A gateway key's product reached its runs-per-minute limit. Retry after the `Retry-After` header.
    + 
    + #### `uploads_rate_limited` {#uploads-rate-limited}
    + `429`, retry. Your org reached its presigned uploads per minute. Retry after the `Retry-After` header.
    + 
    + #### `product_busy` {#product-busy}
    + `429`, retry. Your product's concurrency slots and its queue are full. Retry after the `Retry-After` header (about 5 s).
    + 
    + #### `internal_error` {#internal-error}
    + `500`, retry. Something went wrong on our side. Retry with the same `Idempotency-Key`; quote `X-Request-Id` to support.
    + 
    + #### `style_unavailable` {#style-unavailable}
    + `503`, retry. This style cannot run on a live key right now (no model behind it). Test keys still return a sample.
    + 
    + #### `uploads_unavailable` {#uploads-unavailable}
    + `503`, retry. Uploads are not available on this deployment right now.
    + 
    + #### `service_unavailable` {#service-unavailable}
    + `503`, retry. The job queue is briefly unavailable; nothing was created or charged. Retry with the same `Idempotency-Key` after the `Retry-After` header.
    + 
    + ## Jobs API errors {#jobs-errors}
    + 
    + The [Jobs API](jobs.md#errors) (`/v2/jobs`, `/v2/models`, `GET /v2/styles`) answers `{"error": {"code", "message", "retryable"}}`, plus `details` for `INVALID_INPUT`, with its own codes:
    + 
    + | HTTP | `code` | `retryable` |
    + |---|---|---|
    + | 422 | [`INVALID_INPUT`](#jobs-invalid-input) | false |
    + | 401 | [`UNAUTHORIZED`](#jobs-unauthorized) | false |
    + | 402 | [`INSUFFICIENT_CREDITS`](#jobs-insufficient-credits) | false |
    + | 403 | [`FORBIDDEN`](#jobs-forbidden) | false |
    + | 404 | [`NOT_FOUND`](#jobs-not-found) | false |
    + | 409 | [`IDEMPOTENCY_KEY_REUSED`](#jobs-idempotency-key-reused) | false |
    + | 409 | [`JOB_NOT_CANCELLABLE`](#jobs-job-not-cancellable) | false |
    + | 429 | [`RATE_LIMITED`](#jobs-rate-limited) | true |
    + | 429 | [`PRODUCT_BUSY`](#jobs-product-busy) | true |
    + | 429 | [`END_USER_LIMIT_REACHED`](#jobs-end-user-limit-reached) | true |
    + | 500 | [`INTERNAL_ERROR`](#jobs-internal-error) | true |
    + | 503 | [`SERVICE_UNAVAILABLE`](#jobs-service-unavailable) | true |
    + 
    + #### `INVALID_INPUT` {#jobs-invalid-input}
    + `422`, do not retry. The body or a header is wrong; `details[]` lists each `{field, reason}`. The run endpoints (`POST /v2/runs`, `POST /v2/styles/{style}`) answer the same code with `400` when a `resolution` or the number of files does not fit the style.
    + 
    + #### `UNAUTHORIZED` {#jobs-unauthorized}
    + `401`, do not retry. Missing, unknown, revoked or expired API key.
    + 
    + #### `INSUFFICIENT_CREDITS` {#jobs-insufficient-credits}
    + `402`, do not retry. The product's wallet cannot cover the job. Add funds, then retry.
    + 
    + #### `FORBIDDEN` {#jobs-forbidden}
    + `403`, do not retry. The key is valid but may not do this. Rolling out: a key with the Read only permission submitting or cancelling, or a key limited to other styles (see [API keys](concepts.md#api-keys)). Not returned before that. 
    + #### `NOT_FOUND` {#jobs-not-found}
    + `404`, do not retry. No such job, model or endpoint for this key.
    + 
    + #### `IDEMPOTENCY_KEY_REUSED` {#jobs-idempotency-key-reused}
    + `409`, do not retry. This `Idempotency-Key` was already used with a different body. Use a new key for a new job.
    + 
    + #### `JOB_NOT_CANCELLABLE` {#jobs-job-not-cancellable}
    + `409`, do not retry. Only queued jobs can be cancelled.
    + 
    + #### `RATE_LIMITED` {#jobs-rate-limited}
    + `429`, retry. Too many jobs submitted this minute for this product. Retry after the `Retry-After` header.
    + 
    + #### `PRODUCT_BUSY` {#jobs-product-busy}
    + `429`, retry. Your product's concurrency slots and its queue are full. Retry after the `Retry-After` header (about 5 s).
    + 
    + #### `END_USER_LIMIT_REACHED` {#jobs-end-user-limit-reached}
    + `429`, retry. This end user reached the daily job cap. Retry after the `Retry-After` header (resets 00:00 UTC).
    + 
    + #### `INTERNAL_ERROR` {#jobs-internal-error}
    + `500`, retry. Something went wrong on Wandura's side. Retry with the same `Idempotency-Key`.
    + 
    + #### `SERVICE_UNAVAILABLE` {#jobs-service-unavailable}
    + `503`, retry. This style cannot run on a live key right now (test keys still return a sample). Or the job queue is briefly unavailable: nothing was created or charged; retry after `Retry-After` with the same `Idempotency-Key`.
    + 
    + A job that ends without a result (`failed`, `timeout`) is not an HTTP error: its `error.code` is listed under [Statuses](jobs.md#statuses) on the Jobs API page.
  2. 16 Oct 2026 · +4 −0 lines
    Show changes
    ⋯
      | 401 | `api_key_expired` | No | This API key has expired. Create or rotate a key. |
      | 402 | `wallet_empty` | No | The product wallet and the org pool cannot cover this run. Add money, then retry. |
    + | 403 | `insufficient_scope` | No | Rolling out. This key has the Read only permission: it can read runs and styles, not submit, cancel, share or upload. Use a key with the Run styles permission. |
    + | 403 | `style_not_allowed` | No | Rolling out. This key may run only some styles, and this is not one of them. Use a key that lists it, or ask your org admin. |
      | 404 | `style_not_found` | No | No style with that id for your org (or not public). |
      | 404 | `run_not_found` | No | No run with that id for this product. |
    ⋯
      A run that fails after it was accepted is not an HTTP error: the run ends `failed` or `nsfw` with a [free-run reason](concepts.md#free-runs), and you are not charged.
      
    + A `403` only comes from a key made with a permission or a list of allowed styles (see [API keys](concepts.md#api-keys)). A key made without them never gets one.
    + 
      The [Jobs API](jobs.md#errors) uses its own error codes.
  3. 15 Oct 2026 · +5 −0 lines
    Show changes
    ⋯
      | 429 | `end_user_cap_reached` | Yes | This end user reached the daily run cap. Retry after the Retry-After header (resets 00:00 UTC). |
      | 429 | `gateway_rate_limited` | Yes | A gateway key's product reached its runs-per-minute limit. Retry after the Retry-After header. |
    + | 429 | `uploads_rate_limited` | Yes | Your org reached its presigned uploads per minute. Retry after the Retry-After header. |
    + | 429 | `product_busy` | Yes | Your product's concurrency slots and its queue are full. Retry after the Retry-After header (about 5 s). |
      | 500 | `internal_error` | Yes | Something went wrong on our side. Retry with the same Idempotency-Key; quote X-Request-Id to support. |
      | 503 | `style_unavailable` | Yes | This style cannot run on a live key right now (no model behind it). Test keys still return a sample. |
      | 503 | `uploads_unavailable` | Yes | Uploads are not available on this deployment right now. |
    + | 503 | `service_unavailable` | Yes | The job queue is briefly unavailable; nothing was created or charged. Retry with the same Idempotency-Key after the Retry-After header. |
      
    + Submit answers, `429` ones included, carry `X-Concurrency-Limit`, `X-Concurrency-Running` and `X-Concurrency-Waiting`. Each `product_busy` and `gateway_rate_limited` answer is a refusal: it is listed at `GET /v2/queue/refusals`. See [Queue and congestion](queue-and-congestion.md).
    + 
      A run that fails after it was accepted is not an HTTP error: the run ends `failed` or `nsfw` with a [free-run reason](concepts.md#free-runs), and you are not charged.
      
    ⋯
  4. 14 Oct 2026 · Page created
↑↓ selectEnter openEsc close

Send feedback

Your message goes straight to the Wandura team. It includes the address of this page.

0 / 4000