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

# Errors

> Engine error codes, when you'll see them, and how to handle them.

When something goes wrong, the Engine returns a non-2xx HTTP status with a
JSON body of the form:

```json theme={null}
{
  "error": "<error_code>",
  "message": "<human-readable description>",
  "details": { /* optional, structured */ }
}
```

The `error` field is a stable, machine-readable identifier. The `message`
is for humans. Use `error` for routing logic; show `message` (or your own
translation of the code) to end users.

## HTTP status taxonomy

| Status | Meaning                                                                   |
| ------ | ------------------------------------------------------------------------- |
| `400`  | Bad request — malformed JSON, missing required field, invalid value.      |
| `401`  | Unauthenticated — `X-Engine-Key` missing or invalid.                      |
| `403`  | Authenticated but not allowed to perform this operation.                  |
| `404`  | Resource doesn't exist.                                                   |
| `409`  | Conflict — the resource is in a state that prevents this operation.       |
| `422`  | Validation error — schema-level problem with the request.                 |
| `429`  | Rate-limited — back off.                                                  |
| `500`  | Internal server error — unexpected failure. Report it.                    |
| `502`  | Upstream LLM provider returned an error the Engine couldn't recover from. |
| `503`  | Engine is starting up, draining, or otherwise temporarily unavailable.    |
| `504`  | Upstream LLM provider timed out.                                          |

## Common error codes

The Engine emits its own error codes from `src/errors.py`. The most common
ones you'll see:

| Code                    | When                                                                                 |
| ----------------------- | ------------------------------------------------------------------------------------ |
| `INVALID_KEY`           | `X-Engine-Key` is missing or doesn't match.                                          |
| `KEY_ROTATION_PENDING`  | Key was rotated; old key was used during the overlap window. Retry with the new key. |
| `TASK_NOT_FOUND`        | A `task_id` referenced doesn't exist.                                                |
| `HITL_REQUIRED`         | The agent paused on a HITL request; respond with `/hitl/respond` before continuing.  |
| `LLM_PROVIDER_ERROR`    | The upstream LLM provider returned a non-recoverable error.                          |
| `LLM_RATE_LIMITED`      | The upstream LLM provider rate-limited the call.                                     |
| `CONTEXT_OVERFLOW`      | The agent's context exceeded what compaction could recover.                          |
| `PERMISSION_DENIED`     | The agent (or you) tried to do something the permission system blocked.              |
| `MCP_CONNECTION_FAILED` | An Asset Directory connection attempt failed.                                        |
| `MCP_TOKEN_EXPIRED`     | A connection's credentials expired and refresh failed.                               |
| `MIGRATION_REQUIRED`    | The brain database is at an older schema than the running Engine.                    |

## Streaming errors

Inside an SSE stream from `/execute`, errors arrive as events, not HTTP
status codes:

```
event: error
data: {"error": "LLM_PROVIDER_ERROR", "message": "..."}
```

After an error event, the stream is over. You'll need to make a new
`/execute` call to retry or continue.

## Retry guidance

| Code                   | Retry?    | How                                                                         |
| ---------------------- | --------- | --------------------------------------------------------------------------- |
| `LLM_RATE_LIMITED`     | Yes       | Exponential backoff, starting at 1s.                                        |
| `LLM_PROVIDER_ERROR`   | Sometimes | Retry once with the same `task_id`. If it fails again, surface to the user. |
| `KEY_ROTATION_PENDING` | Yes       | Retry with the new key.                                                     |
| `MCP_TOKEN_EXPIRED`    | No        | Reconnect through `/assets/connect` for the affected ref\_id.               |
| `CONTEXT_OVERFLOW`     | No        | Start a fresh task or trim the conversation.                                |
| `INVALID_KEY`          | No        | Fix the key.                                                                |
| `503`                  | Yes       | Back off; the Engine may be starting up.                                    |

## See also

* [Authentication](./auth) for `INVALID_KEY` and rotation errors.
* [Streaming events](./streaming-events) for the `error` event in SSE.
* [Build with the Engine → Error handling](../build-with-engine/error-handling)
  for end-to-end retry patterns.
