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

# Handle API errors

> Classify device and app API failures and correlate requests without parsing human-readable messages.

## Error contract

Device and app API errors include a top-level machine-readable `code`. Existing
`detail`, `message`, `error`, and validation `errors` fields remain available;
their shape depends on the endpoint. Branch on `code`, not message text.

```json theme={null}
{
  "detail": "Device not connected",
  "code": "device_not_connected"
}
```

This contract covers `/api/v1/execution/start_device`, device-proxy routes,
device-session routes, streaming worker-connection routes, and `/api/v1/apps`
routes, including app creation, build upload/finalization, and remote-build runner
availability. The legacy `/api/v1/builds` routes use the same contract.

The partner connections API retains its separate nested `error.code` envelope.
Do not treat a product error as a partner connection error.

## Request correlation

Send a UUID in `X-Request-ID` to correlate your request with Revyl diagnostics.
Revyl echoes its canonical UUID spelling on success and error responses. If the
header is missing or invalid, Revyl generates a UUID. Record the returned header
when reporting a failure. A request ID is **not an idempotency key** and does not
make a repeated mutation safe.

## Codes and recovery

| Code | Meaning and action |
| - | - |
| `device_not_connected` | The worker reports that its device is not connected. Re-check session readiness before sending another action. |
| `upload_expired` | The multipart upload no longer exists. Discard its saved upload state and start a new upload. |
| `worker_gone` | The session's worker route is missing or the worker cannot be reached. Re-check the authorized session state; do not assume a mutation was never applied. |
| `app_exists` | An active app with that name already exists in your organization. The 409 includes `existing_app_id`; use that ID instead of creating another app. |
| `no_runner` | Build admission verified that no eligible runner is active. Wait for capacity before retrying a build submission. |
| `credits_exhausted` | Build admission was denied by the compute-credit gate. Check the payer's credits before retrying. |
| `billing_denied` | Device admission was denied by the billing gate. Resolve the payer's entitlement before retrying. |
| `billing_unavailable` | Billing verification could not complete. Retry only when the requested operation is safe to repeat. |
| `authentication_required` | Provide valid credentials. |
| `access_denied` | The authenticated caller cannot perform this operation. |
| `not_found` | The resource is missing or inaccessible to this caller. The response does not distinguish other organizations' resources. |
| `invalid_request` | Correct the request, including any validation errors, before retrying. |
| `conflict` | Resolve the conflicting state before repeating the operation. |
| `rate_limited` | Respect `Retry-After` when present. |
| `request_timeout` | The request timed out. Read the current state before repeating a mutation. |
| `temporarily_unavailable` | A dependency is temporarily unavailable; preserve the request ID for diagnostics. |
| `worker_error` | The worker request failed without a more specific classification. |
| `internal_error` | Revyl could not complete the request. Preserve the request ID; do not automatically replay a mutation. |

Existing feature-specific codes are preserved. Handle unknown codes as errors
using the HTTP status and retain their request IDs; clients should not require a
closed list of codes.

`Retry-After`, when supplied, is a delay in seconds. It is not a promise that
capacity will be available afterwards. No retry delay is invented when Revyl
does not know one.

An ordinary full device pool can return **200 with an initializing or queued
session**. Likewise, runner availability can return **200 with `available:
false`**. These are status results, not error responses; no `slots_full` error is
implied.

## Recover an existing app

For an app-name conflict, the response retains its existing human-readable detail
and adds the organization-scoped ID:

```json theme={null}
{
  "detail": "An app named \"My app\" already exists in this organization.",
  "code": "app_exists",
  "existing_app_id": "00000000-0000-0000-0000-000000000123"
}
```

The ID comes only from the caller's organization and excludes deleted apps.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.