> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zerogpu.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Every HTTP status, every validation message, every JSONL error code, and how to recover.

This page is a catalog of every error code you can encounter when using the
Batch and Files APIs, what each one means, and what to do about it.

## Error response shape

All errors (other than CORS preflight) return JSON in the OpenAI-compatible
envelope:

```json theme={null}
{
  "error": {
    "message": "Human-readable description.",
    "type":    "invalid_request_error",
    "code":    "invalid_purpose",
    "param":   "purpose",
    "line":    5
  }
}
```

* `message` is always present.
* `type` is always present. Values mirror OpenAI's vocabulary:
  `invalid_request_error`, `authentication_error`, `rate_limit_exceeded`,
  `internal_error`, plus `insufficient_quota` on `429` responses.
* `code` is a machine-readable string (e.g. `invalid_purpose`,
  `file_not_found`, `insufficient_quota`). `null` when not meaningful.
* `param` names the offending parameter (`"model"`, `"messages"`,
  `"input_file_id"`, …) or is `null`.
* `line` is included for JSONL validation failures during
  `POST /v1/batches` to point at the offending line (1-based).

Every response, success or error, also carries an `x-request-id`
response header (format `req_<token>`). Pass it along when reporting
issues to support so the call can be located in logs.

## HTTP status codes

| Status | Meaning                                                                                                                                                                 |
| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `200`  | Success.                                                                                                                                                                |
| `204`  | Success with no body (CORS preflight).                                                                                                                                  |
| `400`  | Your request is malformed or fails validation. Fix and retry.                                                                                                           |
| `401`  | Missing `x-api-key` header.                                                                                                                                             |
| `403`  | Headers were supplied but credentials are invalid, inactive, or don't belong to the supplied project.                                                                   |
| `404`  | The resource doesn't exist, or doesn't belong to your project. Both cases return `404` to avoid leaking cross-project information.                                      |
| `413`  | Uploaded file is larger than 100 MB.                                                                                                                                    |
| `429`  | Rate-limit / insufficient quota. The `error.type` distinguishes: `insufficient_quota` (no balance and no free credits) vs `rate_limit_exceeded` (transient throttling). |
| `500`  | Internal server error. Retry with exponential backoff; if persistent, contact support.                                                                                  |

<Note>
  **Quota errors return <code>429</code>**

  Catch <code>429</code> for both quota exhaustion (<code>error.type:
  "insufficient\_quota"</code>) and rate-limiting (<code>error.type:
  "rate\_limit\_exceeded"</code>). This matches OpenAI's behaviour and works
  with the official SDK's built-in retry logic.
</Note>

## Common synchronous errors

### `401 Unauthorized`, missing headers

```json theme={null}
{ "error": { "message": "Missing authentication headers" } }
```

**Cause:** `x-api-key` was not sent.
**Fix:** Send the required `x-api-key` header on every request. `x-project-id`
is optional, and only needed to scope the request to a specific project.

### `403 Forbidden`, invalid credentials

```json theme={null}
{ "error": { "message": "Invalid credentials" } }
```

**Cause:** The API key doesn't exist, is inactive, or belongs to a different
project than the one in `x-project-id`.
**Fix:** Verify the key, and verify the project UUID matches.

### `429 Insufficient Quota`

```json theme={null}
{
  "error": {
    "message": "You exceeded your current quota, please check your plan and billing details.",
    "type":    "insufficient_quota",
    "code":    "insufficient_quota",
    "param":   null
  }
}
```

**Cause:** Your organization has zero balance and zero free credits.
**Fix:** Top up your balance.

> **Important:** quota is checked at line-execution time. If your balance
> drops to zero **during** a batch, the remaining lines fail with
> `error.code: "insufficient_quota"` in the error file (the JSONL line's
> `error.message` includes the legacy `[legacy:http_420] ` prefix for
> forensics). The batch as a whole still transitions to `completed` once
> all lines have been accounted for.

### `404 Not Found`, file or batch

```json theme={null}
{ "error": { "message": "File file-xxx not found" } }
```

```json theme={null}
{ "error": { "message": "Batch not found: batch_xxx" } }
```

**Cause:** The ID does not exist, or it belongs to another project, or it
has been soft-deleted. The same `404` is returned in all three cases.
**Fix:** Verify the ID and the `x-project-id` you're sending.

***

## Files API errors

| Status | Code                   | When                                                     |
| ------ | ---------------------- | -------------------------------------------------------- |
| `400`  | `invalid_content_type` | Upload request was not `multipart/form-data`.            |
| `400`  | `invalid_multipart`    | The multipart body could not be parsed.                  |
| `400`  | `invalid_purpose`      | `purpose` was anything other than `"batch"`.             |
| `400`  | `missing_file`         | No `file` field in the multipart body.                   |
| `400`  | `empty_file`           | The uploaded file had zero bytes.                        |
| `400`  | `invalid_limit`        | List `limit` query parameter was not a positive integer. |
| `404`  | `file_not_found`       | File does not exist or is not accessible.                |
| `413`  | `file_too_large`       | File exceeded 100 MB.                                    |
| `500`  | `storage_error`        | Backing storage error; retry.                            |

***

## Batches API. `POST /v1/batches` validation errors

When you create a batch, the service streams the entire input JSONL and
validates every line **synchronously** before returning a response. Any
failure returns `400` with the offending `line` (1-based, mirroring
OpenAI's `BatchError.line`). The batch is also persisted with
`status: "failed"` and a populated `errors` list, so subsequent
`GET /v1/batches/{id}` calls can introspect what went wrong.

| `message`                                                          | Cause                                                                                                      |
| ------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------- |
| `input_file_id is required`                                        | Missing or non-string `input_file_id` field.                                                               |
| `endpoint is required`                                             | Missing or non-string `endpoint` field.                                                                    |
| `completion_window must be "24h"`                                  | Anything other than `"24h"` was supplied.                                                                  |
| `Input file not found: <id>`                                       | `input_file_id` doesn't exist or isn't in this project. (404)                                              |
| `Input file object missing: <id>`                                  | DB row exists but the stored object is gone. Report to support. (404)                                      |
| `Input file contains no JSONL lines`                               | Uploaded file is empty or only whitespace.                                                                 |
| `Input file exceeds maximum size of 209715200 bytes`               | File total > 200 MB.                                                                                       |
| `Input file exceeds maximum of 50000 lines`                        | More than 50,000 lines.                                                                                    |
| `Line N is not valid JSON`                                         | Line N could not be parsed as JSON.                                                                        |
| `Line N must be a JSON object`                                     | Line N parsed as an array or primitive.                                                                    |
| `Line N exceeds maximum size of 1048576 bytes`                     | Line N > 1 MB.                                                                                             |
| `Line N is missing custom_id`                                      | Missing or non-string `custom_id`.                                                                         |
| `Line N duplicates custom_id "X"`                                  | A previous line already used custom\_id `X`.                                                               |
| `Line N method must be "POST"`                                     | `method` (case-insensitive) was not `"POST"`.                                                              |
| `Line N is missing url`                                            | Missing or non-string `url`.                                                                               |
| `Line N url "X" is not an allowed batch endpoint`                  | `url` is not `/v1/chat/completions`, the only [supported batch endpoint](/docs/batch/supported-endpoints). |
| `Line N url "X" does not match the batch endpoint "Y"`             | Lines in the file disagree on `url`.                                                                       |
| `Line N is missing body`                                           | `body` is not a non-empty object.                                                                          |
| `Line N has stream=true; streaming is not supported in batch mode` | `body.stream === true`.                                                                                    |
| `endpoint "X" does not match the url "Y" used by the input file`   | The `endpoint` field on the create request disagrees with what's in the file.                              |

Validation errors carry `line` in the JSON body so you can locate and
fix the offending line:

```json theme={null}
{
  "error": {
    "message": "Line 5 duplicates custom_id \"req-1\"",
    "type":    "invalid_request_error",
    "code":    "invalid_request_error",
    "param":   null,
    "line":    5
  }
}
```

***

## Errors that appear in the `error_file_id` JSONL

A line can fail **after** the batch was successfully created. Each failed
line is recorded in the error file with the line's original `custom_id` and
an `error` object. These are the codes you'll see:

| `error.code`            | Triggered when                                                                                                                                                                                      | Recovery                                                                 |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| `invalid_request_error` | The underlying endpoint rejected the line's body (bad shape, missing field, schema violation). Legacy prefix on `error.message`: `[legacy:http_400]` or `[legacy:http_422]`.                        | Fix the line's `body` and re-submit just that line.                      |
| `authentication_error`  | The API key used to submit the batch lost permission mid-flight, was deactivated, or otherwise became unusable. Legacy: `[legacy:http_401]` / `[legacy:http_403]` / `[legacy:api_key_unavailable]`. | Verify key status; issue a new key if needed.                            |
| `not_found_error`       | A resource referenced in the body (e.g. model ID) doesn't exist. Legacy: `[legacy:http_404]`.                                                                                                       | Verify model IDs.                                                        |
| `request_too_large`     | The line body exceeded the per-request size or token budget. Legacy: `[legacy:http_413]`.                                                                                                           | Reduce input length.                                                     |
| `insufficient_quota`    | Organization quota ran out partway through the batch. Legacy: `[legacy:http_420]` / `[legacy:http_429]`.                                                                                            | Top up balance and re-submit the failed lines.                           |
| `rate_limit_exceeded`   | The endpoint throttled the line (non-quota rate limit).                                                                                                                                             | Re-submit the line; transient.                                           |
| `internal_error`        | The upstream service returned 5xx on every retry, or the queue exhausted delivery attempts. Legacy: `[legacy:http_5xx]` / `[legacy:retries_exhausted]` / `[legacy:dlq_exhausted]`.                  | Re-submit the line; transient.                                           |
| `batch_cancelled`       | The parent batch was cancelled via `POST /v1/batches/{id}/cancel` before this line was dispatched.                                                                                                  | The line did not run; submit a fresh batch if you still need the result. |
| `batch_expired`         | The 24-hour completion window elapsed before this line was dispatched.                                                                                                                              | Submit a fresh batch with the remaining lines.                           |

The original ZeroGPU code is preserved as a `[legacy:<old_code>] ` prefix
on `error.message` for forensics. No `error.details` field is emitted -
the relevant upstream detail (if any) is folded into the message.

***

## Recovering from failures

A batch that ends with some failures is **still** `status: "completed"`.
The pattern for handling this is:

1. Read `output_file_id` and write successful results to your data store.
2. Read `error_file_id` and partition by `error.code`:
   * `invalid_request_error`, `not_found_error`, `request_too_large`:
     client-side issues, log, fix the input lines, and resubmit.
   * `internal_error`, `rate_limit_exceeded`: transient, resubmit as-is.
   * `insufficient_quota`, `authentication_error`: operational, fix the
     underlying issue first, then resubmit.
   * `batch_cancelled`, `batch_expired`: the lines never ran; resubmit
     them in a fresh batch.
3. Build a smaller JSONL containing only the lines you want to retry, upload,
   and create a new batch.

Because `custom_id` is preserved end-to-end, retries are easy to deduplicate
into your existing data store.

***

## When to contact support

Reach out to ZeroGPU support if you see:

* Persistent `500` responses from any endpoint.
* `Input file object missing` on a file you just uploaded.
* A batch stuck in `finalizing` for more than 10 minutes.
* A batch that completes but is missing both `output_file_id` and
  `error_file_id` while `request_counts.total` is non-zero.

## Next steps

<CardGroup cols={2}>
  <Card title="Objects & lifecycle →" href="/docs/batch/objects">
    The Batch object, status lifecycle, and every endpoint.
  </Card>

  <Card title="JSONL format →" href="/docs/batch/jsonl-format">
    Line schemas for input, output, and error files.
  </Card>

  <Card title="Quickstart →" href="/docs/batch/getting-started">
    First batch in under 10 minutes, auth, upload, create, download.
  </Card>
</CardGroup>
