> ## 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

> Every error code bap-engine returns and how to handle each.

Non-2xx responses follow this shape:

```json theme={null}
{
  "error": {
    "code": "<stable_code>",
    "message": "<human description>"
  }
}
```

Use `code` for routing logic; show `message` (or your translation of
the code) to your users.

## HTTP status taxonomy

| Status | Meaning                                                                 |
| ------ | ----------------------------------------------------------------------- |
| `400`  | Bad request — malformed JSON, missing field, invalid value.             |
| `401`  | Unauthenticated — `X-Platform-Key` or `X-Admin-Key` missing or invalid. |
| `403`  | Authenticated but the action is denied (policy, missing scope).         |
| `404`  | Resource doesn't exist.                                                 |
| `409`  | Conflict — resource is in a state that prevents the action.             |
| `429`  | Quota exceeded or rate-limited. Retry after some time.                  |
| `500`  | Internal server error. Report it.                                       |
| `502`  | Backend (Docker, engine) returned an unrecoverable error.               |
| `503`  | Orchestrator starting up or out of capacity.                            |
| `504`  | Backend (engine boot, health probe) timed out.                          |

## Error codes

### Auth

| Code                   | Status | Cause                                        | Action       |
| ---------------------- | ------ | -------------------------------------------- | ------------ |
| `INVALID_PLATFORM_KEY` | 401    | Header missing or unknown.                   | Fix the key. |
| `INVALID_ADMIN_KEY`    | 401    | Admin call without (or wrong) `X-Admin-Key`. | Fix the key. |

### Product / engine state

| Code                     | Status | Cause                                                               | Action                                         |
| ------------------------ | ------ | ------------------------------------------------------------------- | ---------------------------------------------- |
| `PRODUCT_NOT_FOUND`      | 404    | Product slug or ID doesn't exist.                                   | Re-register the product.                       |
| `ENGINE_NOT_FOUND`       | 404    | No engine for this `user_id`.                                       | Provision one (or pass `auto_provision=true`). |
| `ENGINE_ALREADY_EXISTS`  | 409    | `POST /engines/provision` against an existing engine.               | Use `/admit` if you want idempotent behavior.  |
| `ENGINE_NOT_RUNNING`     | 409    | `POST /stop` or `/rotate-key` against an engine that isn't running. | Start it first or skip.                        |
| `ENGINE_ALREADY_RUNNING` | 409    | `POST /start` against a running engine.                             | No-op; ignore.                                 |

### Policy

| Code             | Status | Cause                                         | Action                                            |
| ---------------- | ------ | --------------------------------------------- | ------------------------------------------------- |
| `QUOTA_EXCEEDED` | 429    | Product at `max_engines` limit.               | Destroy unused engines or raise the policy limit. |
| `RATE_LIMITED`   | 429    | Per-product RPM exceeded.                     | Back off; respect `Retry-After` if present.       |
| `POLICY_DENIED`  | 403    | Action not permitted by the product's policy. | Check the policy; update if needed.               |

### Resource exhaustion

| Code              | Status | Cause                                             | Action                                          |
| ----------------- | ------ | ------------------------------------------------- | ----------------------------------------------- |
| `PORT_EXHAUSTION` | 503    | No free port in `[ORCH_PORT_MIN, ORCH_PORT_MAX]`. | Widen the port range or destroy unused engines. |

### Backend

| Code            | Status | Cause                                                           | Action                                                        |
| --------------- | ------ | --------------------------------------------------------------- | ------------------------------------------------------------- |
| `BACKEND_ERROR` | 502    | Docker / subprocess returned an error.                          | Check the orchestrator logs; usually transient.               |
| `BOOT_TIMEOUT`  | 504    | Engine didn't reach `/health: ok` within `ORCH_BOOT_TIMEOUT_S`. | Check the engine's logs. The engine's startup is misbehaving. |

### Internal

| Code             | Status | Cause                  | Action                                        |
| ---------------- | ------ | ---------------------- | --------------------------------------------- |
| `INTERNAL_ERROR` | 500    | Unexpected server bug. | File an issue with the request and timestamp. |

## Retry guidance

| Code                          | Retry?         | How                                                                              |
| ----------------------------- | -------------- | -------------------------------------------------------------------------------- |
| `RATE_LIMITED`                | Yes            | Exponential backoff. Honor `Retry-After`.                                        |
| `QUOTA_EXCEEDED`              | No             | Hard limit; fix the policy or destroy engines.                                   |
| `BACKEND_ERROR`               | Once           | Could be a transient Docker hiccup. After one failed retry, surface to the user. |
| `BOOT_TIMEOUT`                | No             | The engine itself is broken. Investigate.                                        |
| `503` (orchestrator starting) | Yes            | Back off 1–5s.                                                                   |
| `INVALID_PLATFORM_KEY`        | No             | Fix the key.                                                                     |
| `ENGINE_NOT_FOUND`            | No (in lookup) | Provision instead.                                                               |

## Error envelope examples

```json theme={null}
{
  "error": {
    "code": "QUOTA_EXCEEDED",
    "message": "Product 'demo' at max_engines limit (1000)."
  }
}
```

```json theme={null}
{
  "error": {
    "code": "BOOT_TIMEOUT",
    "message": "Engine 5c2f... did not become healthy within 60s."
  }
}
```

The `error.message` is for humans; don't depend on its exact text.

## See also

* [Auth](./auth) — `INVALID_*` codes.
* [Policy](../policy) — quota and rate-limit codes.
* [Lifecycle](../lifecycle) — engine state codes.
