# Errors

A **request error** is an HTTP status of 400 or above: nothing was started and nothing is held. A generation that is accepted and then stops is not a request error: it ends `failed`, free, see [Why a generation failed](https://docs.vidlab.ai/why-a-generation-failed).

Every request error has the same shape. Every answer carries an `X-Request-Id` header: quote it to support.

```json
{
  "error": {
    "message": "`resolution` must be one of: 480p, 720p, 1080p.",
    "type": "invalid_request_error",
    "code": "invalid_input"
  }
}
```

- `code` is stable: branch on it. `message` is written for people and can change.
- `type` groups codes: `invalid_request_error`, `authentication_error`, `permission_error`, `rate_limit_error`, `api_error`, `service_unavailable_error`.
- Some errors add fields beside `error` (`price_usd`, `wallet`, `limits`, `observed`, `plan`, `reset_at`, `request_id`).
- `429` and `503` answers carry a `Retry-After` header in seconds when there is a known wait.

| Status | Code | When | Fix |
| - | - | - | - |
| 400 | `invalid_json` | The body is not a JSON object. | Send a JSON object with `Content-Type: application/json`. |
| 400 | `invalid_request` | A field has the wrong type, is too long, or is not an input field at all. The message names the field. | Fix or remove that field. |
| 400 | `invalid_input` | The model does not take this input: a field it does not list, a value not in `values`, out of range, too many images, a missing required field. The message names the field and the allowed values. | Read `GET /v1/models/{id}` and send only its `params`. |
| 400 | `variant_unavailable` | This model is not offered with these options right now. The message lists the priced options. | Pick one of the listed options. |
| 400 | `invalid_limit`, `invalid_status`, `invalid_model`, `invalid_starting_after` | A query value of `GET /v1/generations` is not valid. | See [List generations](https://docs.vidlab.ai/get-the-result#list-generations). |
| 400 | `max_price_exceeded` | The price is over `max_price_usd`, the older name of the cap. `price_usd` has the price. | Use `max_cost_usd`. |
| 400 | `invalid_idempotency_key` | The `Idempotency-Key` is not 8 to 100 of `A-Z a-z 0-9 _ : . -`. | Send a valid key, such as a UUID. |
| 400 | `empty_file`, `upload_interrupted` | An upload has no bytes, or the connection ended before the whole file arrived. | Send the whole file as the request body. |
| 401 | `unauthorized` | The key is missing, unknown or revoked. | Send `Authorization: Bearer vidlab_sk_...` with a live key. |
| 401 | `key_expired` | The key's expiry date has passed. | Create a new key under API keys. |
| 402 | `key_quota_exceeded` | The key reached its own spend quota. | Raise the key's quota, or use another key. |
| 403 | `ip_not_allowed` | The key has an address list and this address is not on it. | Add the address to the key's list, or use another key. |
| 403 | `model_not_allowed` | The key has a model list and this model is not on it. | Allow the model on the key, or use another key. |
| 403 | `api_key_required` | Generations need a `vidlab_sk_` key; another kind of token was sent. | Use an API key from the dashboard. |
| 403 | `api_key_scope_forbidden` | The key is scoped and this call needs another scope. | Use a full-access key. |
| 403 | `developer_access_forbidden` | This account does not have API access yet. | Contact support@vidlab.ai. |
| 404 | `model_not_found` | No listed model has that id. | List ids with `GET /v1/models`. |
| 404 | `generation_not_found` | This account has no generation with that id. | Use a key of the account that started it. |
| 404 | `output_not_found` | `/content/{n}` is past the files the generation made. | Use a smaller number. |
| 404 | `not_found` | No such endpoint. | Check the method and path. |
| 405 | `method_not_allowed` | Wrong method for this path. | Use the method in the [Quickstart](https://docs.vidlab.ai/quickstart). |
| 409 | `not_ready` | `/content` was asked for before the generation succeeded. | Poll until `succeeded`, then retry after `Retry-After`. |
| 409 | `generation_failed` | `/content` was asked for a failed generation. | Read its `error`. |
| 413 | `request_too_large` | The JSON body is over 2 MB. | Send files as links or [uploads](https://docs.vidlab.ai/uploads), not inline. |
| 413 | `file_too_large` | An upload is over the limit for its kind. | Send a smaller file. |
| 415 | `unsupported_media_type` | An upload is not a picture, video or sound we take, or is not what its `Content-Type` says. | Send a supported type. |
| 422 | `max_cost_exceeded` | No route can run the call for `max_cost_usd` or less. `price_usd` has the price. | Raise `max_cost_usd`, or lower the resolution or duration. |
| 422 | `max_wait_exceeded` | No route is expected to finish within `max_wait_s`. `estimated_seconds` has the usual time. | Raise `max_wait_s`. |
| 429 | `rate_limited` | Too many catalogue, quote or upload calls from your address or account. | Wait for `Retry-After`. |
| 429 | `read_rate_limited` | This key read (polled, listed, fetched files) over 1,200 times this minute. | Wait for `Retry-After`; poll every few seconds, or read many at once with `GET /v1/generations`. |
| 429 | `request_per_minute` | This key started too many generations this minute. | Wait for `Retry-After`, or spread calls out. |
| 429 | `request_per_minute_user` | Your account started too many generations this minute, across keys. | Wait for `Retry-After`. |
| 429 | `request_per_day` | Your account reached its daily request limit. | Wait for `reset_at` (midnight UTC). |
| 429 | `api_wallet_exhausted` | The balance does not have room to reserve this price. `wallet` has the balance. | Add credit, or wait for running generations to finish. |
| 429 | `api_key_budget_exhausted` | This key reached its monthly budget. | Use another key, or wait for the next cycle. |
| 429 | `too_many_uploads` | Too many uploads are running at once. | Wait for one to finish, then try again. |
| 429 | `daily_upload_limit` | The account has uploaded its daily share. | Wait until tomorrow, or send links. |
| 503 | `api_wallet_reconciliation_required` | The balance is held for a billing review. | Contact support. Do not retry until it is cleared. |
| 503 | `model_unavailable` | The model is busy right now. | Retry after `Retry-After`. |
| 500, 502 | `api_error`, `internal_error`, `unavailable` | Something went wrong on our side. | Retry with the same `Idempotency-Key` after a short wait. |

## Retrying safely

- Retry `5xx`, `429` and `model_unavailable` after the `Retry-After` wait (or a short backoff). Retry other `4xx` errors only after you change the request.
- Always send an `Idempotency-Key` on `POST /v1/generations`. A retry with the same key returns the generation the first call made, so a lost answer never starts a second, paid one.
- A failed generation is final. To try again, submit a new one with a new `Idempotency-Key`.
