# Errors

> Every refusal is {error, message} with a fixed error code.

Every refusal has the same shape: an HTTP status, and a JSON body with a fixed `error` code and a fixed sentence.

## The error shape

```json
{
  "error": "role_required",
  "message": "this needs the client_admin role"
}
```

- **`error`** is one of a fixed set, below. Branch on it, not on the message.
- **`message`** is a fixed sentence for people. It never carries a key, a token, a body, or the value that was refused.
- Some refusals add fields: `parameter` names the query parameter that was wrong, `attribute` the body field, `limit` and `retry_after` the limit that was reached.
- A refusal is sent with `Cache-Control: no-store`, and with `Retry-After` where waiting will help.

## Error codes

### 400

| Code        | Means                                                               | Do                                                    |
| ----------- | ------------------------------------------------------------------- | ----------------------------------------------------- |
| `bad_query` | A path or query parameter, or a body field, is missing or not valid | Fix the request. `parameter` says which, where it can |

LightHouse answers a request that fails validation with **400 `bad_query`**, never with 422.

### 401 and 403

| Code                           | Status | Means                                                                          | Do                                                                   |
| ------------------------------ | ------ | ------------------------------------------------------------------------------ | -------------------------------------------------------------------- |
| `unauthenticated`              | 401    | No session and no valid key: missing, revoked, expired, or from another tenant | Sign in, or issue a key in this tenant                               |
| `mfa_required`                 | 403    | The session does not prove a second factor                                     | Sign in again with a second factor                                   |
| `not_authorised`               | 403    | This identity has no grant in this tenant                                      | Ask a `client_admin` for access                                      |
| `role_required`                | 403    | The route needs a higher role                                                  | See [Roles](/api/roles/)                                             |
| `person_required`              | 403    | The route acts only for a signed-in person; a machine key never can            | Do it in LightHouse, as a person                                     |
| `commands_disabled`            | 403    | This command type is not switched on for your tenant                           | A `client_admin` switches it on under **Setup and admin → Commands** |
| `managed_by_aal`               | 403    | This provider connection is managed by Atlantic                                | Ask Atlantic to change it                                            |
| `assistant_off`, `avatars_off` | 403    | The feature is switched off for your tenant                                    | A `client_admin` switches it on                                      |

### 404

| Code             | Means                                                            |
| ---------------- | ---------------------------------------------------------------- |
| `not_found`      | No such resource in your tenant                                  |
| `unknown_client` | No tenant at this address                                        |
| `flow_not_found` | A connector approval has expired (after 15 minutes); start again |

### 409

| Code                                                                                                                                                      | Means                                                                                                |
| --------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| `version_conflict`                                                                                                                                        | What you are changing changed since you read it. Read it again, then write                           |
| `conflict`                                                                                                                                                | That already exists                                                                                  |
| `connector_coming`                                                                                                                                        | The connector is not built yet. See [Connect your estate](/connect/)                                 |
| `route_unavailable`                                                                                                                                       | That way of connecting is not available for this connector yet                                       |
| `adoption_closed`                                                                                                                                         | The marketplace listing is not open to new adoption                                                  |
| `details_required`, `invoice_terms`, `invoice_collection`, `no_customer`, `no_platform_price`, `invoice_not_open`, `nothing_pending`, `mirror_not_needed` | Billing: the step you asked for does not apply to your tenant’s terms or state; the message says why |

### 422

| Code               | Means                                                                                                       |
| ------------------ | ----------------------------------------------------------------------------------------------------------- |
| `invalid`          | The body is not a JSON object, is too large, or a field’s value is not allowed. `attribute` names the field |
| `guard_rail`       | The change breaks a rule LightHouse fixes, such as a key’s expiry or the staff role. `rule` names it        |
| `provider_refused` | The provider would not accept the credential or approval; the message names the provider                    |
| `secret_refused`   | A field that should hold an identifier looks like a credential                                              |

### 429

| Code               | Means                               | Do                       |
| ------------------ | ----------------------------------- | ------------------------ |
| `assistant_capped` | The assistant has reached its limit | Wait, or raise its limit |

See [Rate limits](/api/rate-limits/).

### 5xx

| Code                                                                                                                                         | Status | Means                                                                                             | Do                                        |
| -------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------- | ----------------------------------------- |
| `internal`                                                                                                                                   | 500    | A fault on our side. Nothing was returned                                                         | Retry later; tell Atlantic if it persists |
| `stripe_failed`                                                                                                                              | 502    | The payment provider did not accept the request; nothing was charged                              | Retry                                     |
| `deployment_waking`                                                                                                                          | 503    | Your ephemeral deployment is waking (“Waking your deployment…”)                                   | Retry after `Retry-After` (5 seconds)     |
| `tier_b_unavailable`                                                                                                                         | 503    | Your content cannot be read in place right now: your deployment’s content reader is not connected | Check the reader is running               |
| `provider_unavailable`                                                                                                                       | 503    | The provider could not be reached                                                                 | Retry shortly                             |
| `secret_store_unavailable`                                                                                                                   | 503    | Your tenant’s key is not available, so a credential cannot be sealed                              | Retry; tell Atlantic if it persists       |
| `commands_unavailable`, `assistant_unavailable`, `avatars_unavailable`, `billing_unavailable`, `exports_unavailable`, `identity_unavailable` | 503    | That part of LightHouse is not set up where your tenant runs, or not reachable                    | Retry later; tell Atlantic if it persists |

## Command refusals

A handler command that LightHouse accepted can still be refused by your deployment. That refusal is not an HTTP error: the command’s status becomes `refused`, with a `reason` from a fixed set, such as `type-disabled`, `role`, `not-an-approver`, `bounds`, `raise-not-allowed`, `not-waiting`, `evidence-changed` or `gate-not-passed`. Read it with `GET /api/v1/commands/{command_id}`. See [Handlers](/concepts/handlers/).

## In MCP

A tool refusal arrives as a tool error carrying the same sentence, for example for `role_required`. Any other failure arrives as a generic message that quotes nothing. See [MCP](/mcp/).

Source: https://docs.atlanticagenticlabs.com/api/errors/
