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

# Operator: errors and conventions

> The error taxonomy and which codes are retryable, idempotency on the two routes that accept it, and cursor pagination.

## Errors

Every failure carries a stable machine-readable `code`. Branch on the code,
never on the message; messages are written for humans and will change. The
TypeScript client raises one `OperatorError` subclass per class below.

| Class                 | Status | Codes                                                                                                                                                                                                                      |
| --------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `InvalidRequestError` | 400    | `invalid_cursor`, `invalid_status`, `invalid_space_id`, `space_id_required`, `invalid_product`, `invalid_prompt`, `invalid_mode`, `invalid_idempotency_key`, `invalid_url`, `unknown_event_type`, `duplicate_endpoint_url` |
| `AuthenticationError` | 401    | `invalid_api_key`                                                                                                                                                                                                          |
| `PermissionError`     | 403    | `insufficient_scope`, `product_not_entitled`                                                                                                                                                                               |
| `NotFoundError`       | 404    | `device_not_found`, `task_not_found`, `session_not_found`, `webhook_endpoint_not_found`, `escalation_not_found`                                                                                                            |
| `ConflictError`       | 409    | `device_busy`, `device_not_ready`, `takeover_conflict`, `idempotency_conflict`                                                                                                                                             |
| `GoneError`           | 410    | `device_terminated`, `session_expired`                                                                                                                                                                                     |
| `RateLimitError`      | 429    | `rate_limited`, `quota_exceeded`                                                                                                                                                                                           |
| `ServiceError`        | 5xx    | `provider_error`, `browser_unavailable`, `signing_unavailable`                                                                                                                                                             |

Only six codes are retryable, in the sense that waiting and re-sending the
same request can succeed: `rate_limited`, `quota_exceeded`, `device_busy`,
`device_not_ready`, `provider_error`, `browser_unavailable`. Everything else
is a verdict.

<Note>
  A 404 means "no such row **in your organization**". A row in another
  organization answers identically, so a 404 is never evidence that an id does
  not exist.
</Note>

## Idempotency

Exactly two routes accept an `Idempotency-Key` header: `POST /v1/devices` and
`POST /v1/devices/{id}/tasks`. A create that times out and is retried with the
same key replays the first result instead of provisioning a second browser or
submitting a second task.

Derive your key from the work, not from a clock; a generated key dies with the
process that made it. `provision:${orderId}` survives a crash,
`${Date.now()}` does not. Reusing a key with a different body is
`409 idempotency_conflict`. No other route accepts the header.

## Pagination

Lists are keyset-paginated with an opaque cursor.

* `next_cursor` is server state. Do not parse it, construct one, or persist it
  across deploys; a tampered cursor is `400 invalid_cursor`.
* Page size defaults to 20 and is clamped to 100.
* The TypeScript client exposes lists as async iterators (per item, per page,
  or `collect(n)`), so most code never touches a cursor.

```ts theme={null}
for await (const device of omega.devices.list({ status: "active" })) { /* … */ }

for await (const page of omega.audit.list({ eventType: "operator.task.failed.v1" }).pages()) {
  await sink.writeBatch(page.data);
}
```
