Change history
Every change to this page, newest first. Expand an entry to see what changed.
- 19 Oct 2026 · +2 −2 lines
Show changes
⋯ #### `daily_cap_reached` {#daily-cap-reached} - `429`, do not retry. **Rolling out.** Your product reached its daily spend cap: live charges plus open holds for the current Vietnam calendar day (resets 00:00 UTC+7). Nothing was created or charged. The body carries `cap_usd`, the cap in USD, and there is no `Retry-After`. Wait for the new day, or ask your org admin to raise the cap. + `429`, do not retry. Your product reached its daily spend cap: live charges plus open holds for the current Vietnam calendar day (resets 00:00 UTC+7). Nothing was created or charged. The body carries `cap_usd`, the cap in USD, and there is no `Retry-After`. Wait for the new day, or ask your org admin to raise the cap. #### `gateway_rate_limited` {#gateway-rate-limited} ⋯ #### `DAILY_CAP_REACHED` {#jobs-daily-cap-reached} - `429`, do not retry. **Rolling out.** This product reached its daily spend cap (the day resets at 00:00 Vietnam time, UTC+7). Nothing was created or held. Wait for the new day, or ask your org admin to raise the cap. See [Wandura C1.1](wandura-c1-1.md#errors). + `429`, do not retry. This product reached its daily spend cap (the day resets at 00:00 Vietnam time, UTC+7). Nothing was created or held. Wait for the new day, or ask your org admin to raise the cap. See [Wandura C1.1](wandura-c1-1.md#errors). #### `INTERNAL_ERROR` {#jobs-internal-error} ⋯
- 19 Oct 2026 · +8 −0 lines
Show changes
⋯ | 415 | [`unsupported_content_type`](#unsupported-content-type) | No | | 429 | [`end_user_cap_reached`](#end-user-cap-reached) | Yes | + | 429 | [`daily_cap_reached`](#daily-cap-reached) | No | | 429 | [`gateway_rate_limited`](#gateway-rate-limited) | Yes | | 429 | [`uploads_rate_limited`](#uploads-rate-limited) | Yes | ⋯ `429`, retry. This end user reached the daily run cap. Retry after the `Retry-After` header (resets 00:00 UTC). + #### `daily_cap_reached` {#daily-cap-reached} + `429`, do not retry. **Rolling out.** Your product reached its daily spend cap: live charges plus open holds for the current Vietnam calendar day (resets 00:00 UTC+7). Nothing was created or charged. The body carries `cap_usd`, the cap in USD, and there is no `Retry-After`. Wait for the new day, or ask your org admin to raise the cap. + #### `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. ⋯ | 429 | [`PRODUCT_BUSY`](#jobs-product-busy) | true | | 429 | [`END_USER_LIMIT_REACHED`](#jobs-end-user-limit-reached) | true | + | 429 | [`DAILY_CAP_REACHED`](#jobs-daily-cap-reached) | false | | 500 | [`INTERNAL_ERROR`](#jobs-internal-error) | true | | 503 | [`SERVICE_UNAVAILABLE`](#jobs-service-unavailable) | true | ⋯ `429`, retry. This end user reached the daily job cap. Retry after the `Retry-After` header (resets 00:00 UTC). + #### `DAILY_CAP_REACHED` {#jobs-daily-cap-reached} + `429`, do not retry. **Rolling out.** This product reached its daily spend cap (the day resets at 00:00 Vietnam time, UTC+7). Nothing was created or held. Wait for the new day, or ask your org admin to raise the cap. See [Wandura C1.1](wandura-c1-1.md#errors). + #### `INTERNAL_ERROR` {#jobs-internal-error} `500`, retry. Something went wrong on Wandura's side. Retry with the same `Idempotency-Key`. ⋯
- 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.
- 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.
- 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. ⋯
- 14 Oct 2026 · Page created