# Errors

> Every failure is a typed code with an HTTP status and a retryable flag — the same across HTTP, MCP, the SDK and the CLI.

Errors have one shape:

```json
{
  "error": { "code": "BUSY", "message": "profile already has a live session", "retryable": true, "details": { "session_id": "…" } },
  "correlation_id": "6f1c…"
}
```

Branch on `code`, never on `message`. `details` carries typed facts when there are any (the busy session, the field that failed validation, the limit that was reached). Quote `correlation_id` when you contact support or file feedback.

## Codes

| Code | HTTP | Retryable | Meaning |
|---|---|---|---|
| `UNAUTHENTICATED` | 401 | no | Missing, invalid, revoked or expired key |
| `FORBIDDEN` | 403 | no | The key or its member lacks the capability |
| `REAUTH_REQUIRED` | 403 | no | A sensitive action needs a fresh sign-in in the app |
| `NOT_FOUND` | 404 | no | Nothing visible to you at that id |
| `VALIDATION_FAILED` | 422 | no | Input does not match the schema; `details` lists fields |
| `CONFLICT` | 409 | no | Stale `expected_revision`, or a reused `command_id` with another body |
| `BUSY` | 409 | yes | The resource is in use (for example the profile is open) |
| `STALE_GRANT` | 410 | no | A ticket or grant was superseded |
| `QUOTA_EXCEEDED` | 429 | no | A workspace limit is reached |
| `RATE_LIMITED` | 429 | yes | Too many requests; wait for `Retry-After` |
| `CAPACITY_UNAVAILABLE` | 503 | yes | No capacity right now |
| `DEPENDENCY_UNAVAILABLE` | 503 | yes | A service Browlix depends on did not answer |
| `UNSUPPORTED` | 501 | no | Not supported here (for example by this worker version) |
| `NETWORK_UNHEALTHY` | 424 | no | The proxy or network check failed |
| `INTEGRITY_FAILED` | 422 | no | Data failed an integrity check |
| `OUTCOME_UNKNOWN` | 202 | yes | Accepted, but the result could not be confirmed; check the receipt |
| `RECOVERY_REQUIRED` | 409 | no | The last session ended uncertainly; [recover it](https://browlix.com/en/docs/sessions#recovering) first |
| `SYNC_PENDING` | 409 | yes | Another machine holds newer profile data |
| `FENCED` | 409 | no | Your app hold on a session is no longer current; [acquire it again](https://browlix.com/en/docs/app-sessions#fencing) |
| `UNKNOWN` | 500 | no | Unexpected; report it with the `correlation_id` |

## Retrying

Retry only codes marked retryable, with backoff, and — for commands — with the **same** `command_id`. The SDK does this for you on reads and same-id commands, honouring `Retry-After` up to 10 seconds.
