# Errors (/api-reference/errors)



Zumik returns errors in the exact OpenAI envelope on `/v1`, and the same shape on `/v2` for consistency. Existing OpenAI SDK error handling works unchanged. Error bodies never leak internal details.

## The error envelope [#the-error-envelope]

Every error response is a single `error` object:

```json
{
  "error": {
    "message": "Missing or invalid API key.",
    "type": "invalid_request_error",
    "code": "invalid_api_key",
    "param": "messages"
  }
}
```

<ResponseField name="error.message" type="string">
  A human-readable description of what went wrong. Safe to surface to a developer; never contains secrets or internal stack detail.
</ResponseField>

<ResponseField name="error.type" type="string">
  The error class. One of `invalid_request_error`, `rate_limit_error`, `insufficient_quota`, `api_error`, or `idempotency_conflict`.
</ResponseField>

<ResponseField name="error.code" type="string | null">
  A stable machine-readable code for branching in client logic. Present on most errors; omitted for generic validation and not-found cases.
</ResponseField>

<ResponseField name="error.param" type="string | null">
  The request field that caused a validation error, when one applies (for example `messages` or `input_file_id`). Omitted otherwise.
</ResponseField>

Branch on `type` and `code`, not on `message` text, which may be refined over time.

## Error-code table [#error-code-table]

| HTTP | `type`                  | `code`                    | Meaning                                                                                                                 | How to fix                                                                                                           |
| ---- | ----------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| 400  | `invalid_request_error` | (none)                    | The request body or a parameter is malformed or missing. `param` names the offending field.                             | Correct the field named in `param` and retry.                                                                        |
| 401  | `invalid_request_error` | `invalid_api_key`         | The bearer token is missing, malformed, or unknown.                                                                     | Send a valid `Authorization: Bearer zk_live_...` header. See [authentication](/api-reference/authentication).        |
| 401  | `invalid_request_error` | `invalid_credentials`     | Console login credentials were wrong (login plane only).                                                                | Re-authenticate in the [console](https://console.zumik.ai). Not used by the bearer-key API.                          |
| 402  | `invalid_request_error` | `credits_required`        | The project's prepaid credit balance is empty, so inference is refused.                                                 | Add credits in the console, then retry.                                                                              |
| 403  | `invalid_request_error` | `insufficient_scope`      | The key authenticated but lacks the scope for this action (for example managing aliases with an inference-only key).    | Use a key with the required scope (for example `admin`).                                                             |
| 403  | `invalid_request_error` | `region_not_allowed`      | The request would route to a region the project's regional policy forbids.                                              | Adjust the regional policy, or send traffic that resolves to an allowed region. See [QoS and policy](/concepts/qos). |
| 404  | `invalid_request_error` | (none)                    | The addressed object (response, conversation, batch, file, model) does not exist for this project.                      | Check the id and that it belongs to the authenticated project.                                                       |
| 409  | `invalid_request_error` | `branch_version_conflict` | An optimistic-concurrency append lost the race: the branch moved since you read its version.                            | Re-read the branch head and replay the append with the current `expected_version`.                                   |
| 409  | `idempotency_conflict`  | (none)                    | A request with the same `Agent-Idempotency-Key` is still in progress.                                                   | Wait for the original to settle, then retry. See [idempotency](/api-reference/idempotency).                          |
| 429  | `insufficient_quota`    | `quota_exceeded`          | A project or per-key spending budget is reached.                                                                        | Raise the cap or enable overage in the console.                                                                      |
| 429  | `rate_limit_error`      | `rate_limit_exceeded`     | The per-key request-rate limit was exceeded (throughput, not budget).                                                   | Back off and retry with exponential backoff. See [rate limits](/guides/rate-limits).                                 |
| 502  | `api_error`             | (none)                    | An upstream provider failed and no fallback path succeeded.                                                             | Retry; if it persists, the resolved provider is degraded.                                                            |
| 504  | `invalid_request_error` | `deadline_exceeded`       | The request's QoS `deadline_ms` elapsed before the provider responded, so Zumik aborted it. The request is not charged. | Raise `deadline_ms` in your [agent hints](/concepts/agent-hints), or accept the slower result.                       |

## Handling retries [#handling-retries]

`429` and `504` are transient and safe to retry with backoff. The `rate_limit_error` type signals a throughput problem (slow down), while `insufficient_quota` signals a money problem (raise the cap or enable overage). To make a retry safe against double-charging, attach an [idempotency key](/api-reference/idempotency). See [idempotency and retries](/guides/idempotency-and-retries) for the full pattern.

A `504` deadline timeout never charges the request, because Zumik bails before the provider result is recorded.
