> The full index of these docs is at [llms.txt](https://u22a8.ai/llms.txt).

# Errors

Every status A8 returns for a failed request, with its body, its cause and its fix, and how each SDK raises it.

An error response carries a status, a JSON body in the format of the interface it answers, and an `x-request-id` header. The body's `code` names the exact condition and `param` names the field to change. An abstention is a successful response with status `200`. See [Accuracy and abstention](https://u22a8.ai/docs/a8-abstention.md).

## 1 Status codes

| Status | `type` on the OpenAI interfaces | `type` on Anthropic Messages | Condition                                                                                                                                                                    |
| ------ | ------------------------------- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `400`  | `invalid_request_error`         | `invalid_request_error`      | The body asks for something A8 refuses, or holds a value outside its range                                                                                                   |
| `401`  | `authentication_error`          | `authentication_error`       | The key is missing, invalid or revoked                                                                                                                                       |
| `403`  | `permission_error`              | `permission_error`           | The key lacks the permission the request needs, or its organization lacks access to A8                                                                                       |
| `404`  | `not_found_error`               | `not_found_error`            | The pinned version names a horizon outside your organization’s stored horizons, the stored response is outside your organization’s store, or the path is outside A8’s routes |
| `405`  | `invalid_request_error`         | `invalid_request_error`      | The route takes another method                                                                                                                                               |
| `413`  | `invalid_request_error`         | `request_too_large`          | The body exceeds the size limit                                                                                                                                              |
| `429`  | `rate_limit_error`              | `rate_limit_error`           | The organization is over its rate limit                                                                                                                                      |
| `500`  | `server_error`                  | `api_error`                  | An error on our side                                                                                                                                                         |
| `503`  | `server_error`                  | `overloaded_error`           | A version, a dependency or key verification is briefly unavailable                                                                                                           |

*Table 1.* Status codes. The SDKs retry `429`, `500` and `503` on their own (Table 4).

An abstention and a filtered request both return `200`, in the slots each API keeps for them. Each interface’s page shows them.

## 2 Error bodies

The OpenAI interfaces return OpenAI's error object. Anthropic Messages returns Anthropic's envelope, with the same `code` and `param` inside it:

**Chat Completions**

```json
{
  "error": {
    "message": "Set the minimum accuracy with the model's tag, as a8:95.",
    "type": "invalid_request_error",
    "param": "min_accuracy",
    "code": "unsupported_parameter",
    "request_id": "req_5b456a869547474cc316c4fc48017e19"
  }
}
```

**Responses**

```json
{
  "error": {
    "message": "Set the minimum accuracy with the model's tag, as a8:95.",
    "type": "invalid_request_error",
    "param": "min_accuracy",
    "code": "unsupported_parameter",
    "request_id": "req_5b456a869547474cc316c4fc48017e19"
  }
}
```

**Anthropic Messages**

```json
{
  "type": "error",
  "error": {
    "type": "invalid_request_error",
    "message": "Set the minimum accuracy with the model's tag, as a8:95.",
    "param": "min_accuracy",
    "code": "unsupported_parameter"
  },
  "request_id": "req_5b456a869547474cc316c4fc48017e19"
}
```

**MCP**

```json
{
  "isError": true,
  "content": [
    {
      "type": "text",
      "text": "model: Tag the model with a level from 0 to 99, as a8:95."
    }
  ]
}
```

Recorded from `a8-1-1@20260715t140322z` on 2026-10-22.

Over MCP, a failed call is a tool result with `isError` set, and its text names the field and the value that passes. See [MCP](https://u22a8.ai/docs/a8-mcp.md).

The bodies below are the Chat Completions form. On the other interfaces the message names that interface’s own fields.

## 3 `400` Bad request

### 3.1 `unsupported_parameter`

The body carries a field A8 refuses: `min_accuracy` or `expected` on Chat Completions, Responses or Anthropic Messages, or `conversation` on Responses.

```json
{
  "error": {
    "message": "Teach through MCP. A request carries the conversation.",
    "type": "invalid_request_error",
    "param": "expected",
    "code": "unsupported_parameter",
    "request_id": "req_0c9e3f1a2b4d5e6f7a8b9c0d1e2f3a4b"
  }
}
```

**Fix:** set the minimum accuracy with the model’s tag, as `a8:95`, and teach through [MCP](https://u22a8.ai/docs/a8-mcp.md). On Responses, continue a conversation with `previous_response_id`.

### 3.2 `unsupported_value`

A field holds a value outside the set A8 accepts, or the request lacks a part it needs. `param` names the field:

- **Minimum accuracy:** a `model` tag other than a whole number from `0` to `99` (`param` is `model`).
- **Reasoning:** an effort value the interface lacks, or a `thinking.type` other than `adaptive`, `enabled` or `disabled`.
- **Choices:** an `n` outside `1` to `128`.
- **Verdict shape:** the `json_object` response format, a schema other than a JSON Schema object, a score property whose `minimum` is at or above its `maximum`, or a `tool_choice` naming a tool outside `tools`.
- **Request parts:** a request that leaves out its criterion (`param` is `criterion`), a message that leaves out its role, or a subject standard other than a string or a list of strings.
- **Responses fields:** a `store` other than a boolean, a `previous_response_id` or `instructions` other than a string, and an `order`, `limit` or `after` outside its range on `input_items`.

```json
{
  "error": {
    "message": "Tag the model with a level from 0 to 99, as a8:95.",
    "type": "invalid_request_error",
    "param": "model",
    "code": "unsupported_value",
    "request_id": "req_7d1b2c3e4f5a6b7c8d9e0f1a2b3c4d5e"
  }
}
```

**Fix:** send a value from the range the message names. For a missing criterion, the message reads `Send a criterion: what the subject is judged against.` and names where the criterion travels in the interface. The minimum accuracy’s tag is on [Accuracy and abstention](https://u22a8.ai/docs/a8-abstention.md), and each interface’s effort values are on its own page. For a structured verdict, use `json_schema`, which names the fields to answer in.

### 3.3 `missing_subject`

The request holds a criterion and leaves the subject empty: the conversation holds system messages alone, ends with an assistant message or ends with an empty user message. In Responses, `input` is empty or absent.

```json
{
  "error": {
    "message": "End the conversation with the user message to judge.",
    "type": "invalid_request_error",
    "param": "messages",
    "code": "missing_subject",
    "request_id": "req_2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d"
  }
}
```

**Fix:** end the conversation with a user message that holds the subject or the follow-up.

### 3.4 `unsupported_content`

A subject part holds an image, audio or a file. A8 reads text.

```json
{
  "error": {
    "message": "Send the subject as text: a string, or a list of text parts.",
    "type": "invalid_request_error",
    "param": "messages[1].content[0]",
    "code": "unsupported_content",
    "request_id": "req_3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e"
  }
}
```

**Fix:** send the subject as a string, or a list of text parts.

### 3.5 `unknown_subject_key`

The subject is a JSON object with `output` and a key outside `output`, `reference`, `context` and `question`. `param` names the key under `subject`.

```json
{
  "error": {
    "message": "A subject takes output, reference, context and question.",
    "type": "invalid_request_error",
    "param": "subject.answer",
    "code": "unknown_subject_key",
    "request_id": "req_4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f"
  }
}
```

**Fix:** rename the key to one of the four. See [API overview](https://u22a8.ai/docs/api-overview.md#subject-context).

### 3.6 `invalid_json`

The body is something other than a JSON object.

```json
{
  "error": {
    "message": "Send the body as a JSON object.",
    "type": "invalid_request_error",
    "param": null,
    "code": "invalid_json",
    "request_id": "req_4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a"
  }
}
```

**Fix:** send the body as a JSON object.

### 3.7 `unknown_model`

`model` is outside A8’s identifiers, or names a version we have yet to publish.

```json
{
  "error": {
    "message": "Use a8, or an identifier from GET /models.",
    "type": "invalid_request_error",
    "param": "model",
    "code": "unknown_model",
    "request_id": "req_5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a"
  }
}
```

**Fix:** send `a8`, or an identifier the models list names.

### 3.8 `previous_response_not_found`

On Responses, `previous_response_id` names a response outside your organization’s store: an unknown ID, a response stored more than 30 days ago, a deleted response or one sent with `store: false`.

```json
{
  "error": {
    "message": "Previous response eval-3b7e0a12c4d5f6a8 was not found.",
    "type": "invalid_request_error",
    "param": "previous_response_id",
    "code": "previous_response_not_found",
    "request_id": "req_6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b"
  }
}
```

**Fix:** send the `id` of a stored response, or send the earlier items of the conversation in `input`.

### 3.9 `context_length_exceeded`

The request holds more tokens than A8 reads in one request (Table 3).

```json
{
  "error": {
    "message": "The request holds more tokens than A8 reads in one request.",
    "type": "invalid_request_error",
    "param": null,
    "code": "context_length_exceeded",
    "request_id": "req_6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b"
  }
}
```

**Fix:** shorten the subject or the criterion.

## 4 `401` Unauthorized

### 4.1 `invalid_api_key`

The key is missing, invalid or revoked. The message names the cause: a missing key gets the message below, and an invalid or revoked key gets `The API key is invalid or revoked.`

```json
{
  "error": {
    "message": "Send a valid API key as Authorization: Bearer <key>.",
    "type": "authentication_error",
    "param": null,
    "code": "invalid_api_key",
    "request_id": "req_6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b"
  }
}
```

**Fix:** send a current key. A revoked key returns `401` within about a minute of revocation. See [API keys](https://u22a8.ai/docs/api-keys.md).

## 5 `403` Forbidden

### 5.1 `missing_permission`

The key is valid and lacks the permission the request needs. The message names it.

```json
{
  "error": {
    "message": "This request needs the models:explain permission.",
    "type": "permission_error",
    "param": null,
    "code": "missing_permission",
    "request_id": "req_7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c"
  }
}
```

**Fix:** use a key that holds the permission, or grant it to a new key. See [API keys](https://u22a8.ai/docs/api-keys.md#permissions).

### 5.2 `model_not_enabled`

The key is valid and its organization lacks access to A8. Every request from it returns this, over MCP as well, until we turn A8 on for the organization.

```json
{
  "error": {
    "message": "A8 is off for this organization; request it in the console.",
    "type": "permission_error",
    "param": null,
    "code": "model_not_enabled",
    "request_id": "req_1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a"
  }
}
```

**Fix:** request access in **Console → A8**. We write to you when A8 is on for your organization.

## 6 `404` Unknown horizon, response or route

### 6.1 `horizon_not_found`

A pinned identifier names a horizon outside your organization’s stored horizons: a typo, or a provisional horizon that a later teach replaced.

```json
{
  "error": {
    "message": "a8-1-1@20260716t090000z names an unknown horizon.",
    "type": "not_found_error",
    "param": "model",
    "code": "horizon_not_found",
    "request_id": "req_8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d"
  }
}
```

**Fix:** pin an identifier from `GET /models`. See [Horizons](https://u22a8.ai/docs/a8-horizons.md) and [Deprecations](https://u22a8.ai/docs/deprecations.md).

### 6.2 `response_not_found`

On Responses, `GET /responses/{id}`, `GET /responses/{id}/input_items` or `DELETE /responses/{id}` names a response outside your organization’s store: an unknown ID, a response stored more than 30 days ago, a deleted response or one sent with `store: false`.

```json
{
  "error": {
    "message": "Response eval-3b7e0a12c4d5f6a8 was not found.",
    "type": "not_found_error",
    "param": "response_id",
    "code": "response_not_found",
    "request_id": "req_9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e"
  }
}
```

**Fix:** send the `id` of a response stored within the last 30 days.

### 6.3 `route_not_found`

The path is outside A8’s routes. The body is in the format of the interface the path starts with, and the OpenAI format elsewhere.

```json
{
  "error": {
    "message": "POST /eval/v1/verdicts is outside A8's routes.",
    "type": "not_found_error",
    "param": null,
    "code": "route_not_found",
    "request_id": "req_5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b"
  }
}
```

**Fix:** send the request to a route on [API overview](https://u22a8.ai/docs/api-overview.md#base-url-and-routes).

## 7 `405` Wrong method

### 7.1 `method_not_allowed`

The path names a route that takes another method. The `allow` header names it, and so does the message.

```json
{
  "error": {
    "message": "/eval/v1/chat/completions takes POST.",
    "type": "invalid_request_error",
    "param": null,
    "code": "method_not_allowed",
    "request_id": "req_6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b1c"
  }
}
```

**Fix:** send the request with the method the `allow` header names.

## 8 `413` Request too large

### 8.1 `request_too_large`

The body exceeds the size limit in Table 3.

**Fix:** send a smaller body. Split a long subject into requests of their own.

## 9 `429` Too many requests

### 9.1 `rate_limit_exceeded`

The organization is over its requests or tokens per minute. The response carries `retry-after` and the `x-ratelimit-*` headers.

```json
{
  "error": {
    "message": "Rate limit reached for requests per minute. Retry after 2s.",
    "type": "rate_limit_error",
    "param": null,
    "code": "rate_limit_exceeded",
    "request_id": "req_9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e"
  }
}
```

**Fix:** wait for `retry-after`. The SDKs do this on their own. See [Rate limits](https://u22a8.ai/docs/rate-limits.md).

## 10 `500` Internal server error

### 10.1 `internal_error`

An error on our side. The request ID lets us find it.

**Fix:** retry. When it repeats, write to <hello@u22a8.ai> with the `x-request-id`.

## 11 `503` Service unavailable

Each `503` carries a code for its cause, and two of them a `retry-after`:

| `code`                         | Cause                                                             | `retry-after`                |
| ------------------------------ | ----------------------------------------------------------------- | ---------------------------- |
| `horizon_fitting`              | The pinned version is the provisional horizon, still being fitted | `30`                         |
| `upstream_unavailable`         | A dependency is briefly unavailable                               | `5`                          |
| `key_verification_unavailable` | Key verification is briefly unavailable                           | Absent: use your own backoff |

*Table 2.* The causes of a `503`. A request pinned to a provisional horizon waits for its model this way.

```json
{
  "error": {
    "message": "a8-1-1@20260722t101500z is being fitted. Retry after 30s.",
    "type": "server_error",
    "param": "model",
    "code": "horizon_fitting",
    "request_id": "req_0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f"
  }
}
```

**Fix:** retry after the interval. The SDKs do this on their own.

## 12 Request limits

| Limit                         | Value              |
| ----------------------------- | ------------------ |
| Request body                  | TBD (size in MiB)  |
| Tokens in one request         | TBD (input tokens) |
| Properties in a verdict shape | TBD (count)        |
| Text parts in a subject       | TBD (count)        |

*Table 3.* Request limits. A body over its size returns `413`; a request over its tokens returns `400` with the code `context_length_exceeded`.

## 13 Errors in the SDKs

The OpenAI and Anthropic SDKs raise one exception class per status:

| Status             | OpenAI SDK              | Anthropic SDK           |
| ------------------ | ----------------------- | ----------------------- |
| `400`              | `BadRequestError`       | `BadRequestError`       |
| `401`              | `AuthenticationError`   | `AuthenticationError`   |
| `403`              | `PermissionDeniedError` | `PermissionDeniedError` |
| `404`              | `NotFoundError`         | `NotFoundError`         |
| `405`, `413`       | `APIStatusError`        | `APIStatusError`        |
| `429`              | `RateLimitError`        | `RateLimitError`        |
| `500`, `503`       | `InternalServerError`   | `InternalServerError`   |
| Connection failure | `APIConnectionError`    | `APIConnectionError`    |

*Table 4.* The exception each status raises. Both SDKs retry `408`, `409`, `429` and every status from `500` up twice by default, with backoff that honours `retry-after`.

Branch on the body's `code`:

**Python**

```python
import openai

try:
    resp = client.chat.completions.create(**request)
except openai.PermissionDeniedError as error:
    print(error.code)        # "missing_permission"
    print(error.request_id)  # quote it in a support request
except openai.BadRequestError as error:
    print(error.code, error.param)  # the condition, and the field to change
```

**TypeScript**

```typescript
import OpenAI from "openai";

try {
  const resp = await client.chat.completions.create(request);
} catch (error) {
  if (error instanceof OpenAI.PermissionDeniedError) {
    console.log(error.code); // "missing_permission"
    console.log(error.requestID); // quote it in a support request
  } else if (error instanceof OpenAI.BadRequestError) {
    console.log(error.code, error.param); // the condition, and the field
  } else {
    throw error;
  }
}
```

## 14 Request IDs

Every response, an error included, carries an `x-request-id` header, and the error body carries the same ID as `request_id`. Quote it in a support request. See [API overview](https://u22a8.ai/docs/api-overview.md#request-ids) for where each SDK exposes it.

## Next steps

- [API overview](https://u22a8.ai/docs/api-overview.md): What every request to A8 shares, from the base URL and authentication to the request parts and the response fields.
- [Rate limits](https://u22a8.ai/docs/rate-limits.md): How A8 limits requests and tokens per organization, the headers that report the limits, and how to raise them.
- [API keys](https://u22a8.ai/docs/api-keys.md): Create an API key, grant its permissions, authenticate requests with it and revoke it.
