--- title: Errors and status codes | Lightcone description: How the API reports errors, covering HTTP status codes, problem+json bodies, task-level error events, and what to retry. --- The API reports failures at three levels: **HTTP errors** (the request itself failed), **task-level events** (a Task hit a problem while running), and **action results** (an action was accepted but didn’t succeed). Handle all three. ## Error body shape HTTP errors return an [RFC 7807](https://datatracker.ietf.org/doc/html/rfc7807) problem document with `Content-Type: application/problem+json`: ``` { "type": "about:blank", "title": "Payment Required", "status": 402, "detail": "Your organization is out of credits.", "instance": "/computers" } ``` | Field | Meaning | | ---------- | --------------------------------------------------- | | `type` | URI reference identifying the error category | | `title` | Short human-readable summary | | `status` | The HTTP status code, repeated in the body | | `detail` | Optional; specifics about this occurrence | | `instance` | Optional; the path or resource the error relates to | ## Status codes | Status | Meaning | What to do | | ------ | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ | | `400` | Validation error, e.g. passing `start_url` on a desktop task, or an out-of-range parameter | Fix the request; don’t retry as-is | | `401` | Missing or invalid API key | Check `TZAFON_API_KEY`; regenerate the key if needed | | `402` | Out of credits | Top up at [lightcone.ai/dashboard/usage](https://lightcone.ai/dashboard/usage) | | `403` | Key is valid but not allowed to do this | Check the key’s scope and your plan | | `404` | Resource does not exist, or belongs to another organization | Verify the ID and that you’re using the right org’s key | | `429` | Concurrent computer limit for your plan reached | Wait for a session to finish, or retry with backoff | | `500` | Internal error | Retry with backoff; contact support if persistent | | `502` | Bad gateway | Retry with backoff | | `503` | Upstream busy | Retry with backoff | There are two kinds of `402`. Running out of credits returns the standard problem+json body above. A **lapsed subscription** puts your org in read-only mode (reads keep working; creating or running things is blocked) and returns a fixed body instead: `{"error": "light_subscription_lapsed", "status": "read_only"}`. Branch on `error`, not the problem+json `type`, for this case. ## Endpoints that don’t use problem+json Several surfaces return errors in a different shape: - **Lapsed-subscription `402`** returns `{"error": "light_subscription_lapsed", "status": "read_only"}` (see above), not problem+json. - **`/exec` streaming** always responds `HTTP 200` and streams newline-delimited JSON (`application/x-ndjson`). Each line is `{"type": "stdout" | "stderr" | "exit" | "error", ...}`. The stream ends with a single `{"type": "exit", "code": }` line; **code `-1` means the command timed out** or terminated abnormally. Check the `exit` line, not the HTTP status. - **OpenAI-compatible endpoints** return OpenAI-style error bodies, so existing OpenAI SDK error handling works unchanged. - **Anthropic-compatible endpoints** return Anthropic-style bodies: `{"type": "error", "error": {"type": "...", "message": "..."}}`. ## Task-level errors (SSE events) A Task that starts successfully reports problems as **stream events**, not HTTP errors: | Event | Meaning | | -------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `config_error` | The task couldn’t start with the given configuration. Reasons include `model_not_found`, `invalid_kind`, and `api_error` | | `error` | A non-terminal problem; the task is still running and may recover | | `failed` | Terminal failure. Carries `status`, an `error_code` (e.g. `session_expired`, `exec_failed`, `invalid_token`), and a `retryable` flag | | `max_steps` | The task hit its step limit before finishing | | `max_duration` | The task hit its time limit before finishing | | `cancelled` | The task was cancelled | Distinguish `error` (transient, keep listening) from `failed` (terminal). On `failed`, check `retryable` before resubmitting. ``` stream = client.agent.tasks.start_stream(instruction="...", kind="browser") for event in stream: if event.type == "failed": if event.retryable: pass # resubmit the task else: raise RuntimeError(f"Task failed: {event.error_code}") ``` ``` const stream = await client.agent.tasks.startStream({ instruction: "...", kind: "browser" }); for await (const event of stream) { if (event.type === "failed") { if (event.retryable) { // resubmit the task } else { throw new Error(`Task failed: ${event.error_code}`); } } } ``` ## Action-level failures Computer actions (click, navigate, screenshot, …) return `HTTP 200` even when the action itself fails. The `ActionResult` carries the outcome: ``` result = computer.navigate("https://example.com") if result.status != "success": print(f"Navigation failed: {result.error_message}") ``` ``` const result = await client.computers.navigate(id, { url: "https://example.com" }); if (result.status !== "success") { console.error(`Navigation failed: ${result.error_message}`); } ``` Always check `ActionResult.status`. A `200` response only means the API accepted the action, not that it succeeded. ## What to retry | Error | Retry? | How | | -------------------------------------- | --------- | ------------------------------------------------------------------- | | `429` | Yes | Exponential backoff, or wait for a running session to finish | | `500` / `502` / `503` | Yes | Exponential backoff (the SDKs do this automatically) | | `failed` event with `retryable: true` | Yes | Resubmit the task | | `error` event (non-terminal) | No action | Keep consuming the stream; the task may recover | | `400` | No | Fix the request first | | `401` | No | Fix the key first | | `402` | No | Add credits first | | `failed` event with `retryable: false` | No | Fix the underlying cause (e.g. re-authenticate for `invalid_token`) | ## Next steps - [**Troubleshooting**](/guides/troubleshooting/index.md): fixes for common failure modes - [**Production checklist**](/guides/production/index.md): timeouts, retries, and hardening - [**API Reference**](/api/index.md): per-endpoint error details