---
title: 'Errors'
description: 'HTTP errors, failed runs, and tool calls that did not complete'
---
[View as Markdown](https://camelai.com/docs/camelrun/errors.md) · [Docs index for coding agents](https://run.camelai.com/llms.txt)

There are three kinds of failure:

1. **The API refused a request.** An HTTP status and a JSON body. The SDKs throw
   `AgentError` with `status` and `code`.
2. **A run failed.** The request was accepted, and the run ended badly. The
   SDKs' `run()` throws `RunError` with the run, unless `throwOnError: false` (Python `throw_on_error=False`).
3. **A tool call didn't complete** within a run that otherwise went on. The
   model was told, and the run lists it in `toolErrors`. Nothing throws.

## HTTP errors

Error bodies are `{"error": "<message for people>", "code": "<CODE>"}`. The
message may change and the code won't, so switch on `code`. New codes may be
added; treat an unknown one by its status. Every 429 and 503 has `Retry-After`,
and the SDKs honor it and retry.

| Status | `code`                      | Meaning                                                                 | What to do                                                         |
| ------ | --------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------ |
| 400    | `INVALID_REQUEST`           | Malformed or invalid; the message says how                              | Fix the request                                                    |
| 400    | `INVALID_HISTORY`           | An imported `initialMessages` has a message it can't take               | Fix the named message                                              |
| 400    | `FORK_POINT_INVALID`        | A fork's `atMessage` is no index in the history, nor a request id in it | Check the index or id                                              |
| 401    | `UNAUTHORIZED`              | No valid token, or an expired browser token                             | Send a valid key; mint a new browser token                         |
| 402    | `SPEND_LIMIT`               | The agent's spend limit, or the account's monthly cap                   | Raise the limit                                                    |
| 402    | `INSUFFICIENT_CREDIT`       | The account's prepaid credit is spent                                   | Add credit in the console                                          |
| 403    | `FORBIDDEN`                 | The token may not do this, such as a browser token outside its agent    | Use your API key, server-side                                      |
| 404    | `NOT_FOUND`                 | No such agent, request, input or other resource in your account         | Check the id                                                       |
| 409    | `IDEMPOTENCY_CONFLICT`      | The Idempotency-Key or request id was used for another request          | Use a new key for a new request                                    |
| 409    | `IDEMPOTENCY_IN_PROGRESS`   | The first request with this key is still running                        | Retry shortly                                                      |
| 409    | `APPLICATION_CONNECTED`     | Another process serves this agent's tools                               | Close it, pass `takeover: true`, or connect with `attach: false`   |
| 409    | `APPLICATION_NOT_CONNECTED` | The agent's tools need their process and none is connected              | Start it, serve tools over HTTP, or pass `allowDisconnected: true` |
| 409    | `FORK_POINT_RUNNING`        | A fork's `atMessage` is in a turn that hasn't ended                     | Fork at an earlier message, or once it ends                        |
| 409    | `REPLAY_GAP`                | The `Last-Event-ID` is behind the replay buffer                         | Reconnect with `?snapshot=1` and read `/state`                     |
| 409    | `CONFLICT`                  | Another conflict, named in the message                                  | See below                                                          |
| 410    | `GONE`                      | The agent was deleted or expired                                        | Upsert the key again for a fresh agent                             |
| 413    | `TOO_LARGE`                 | The request body is too large                                           | Upload large files first and attach them by path                   |
| 413    | `HISTORY_TOO_LARGE`         | An imported history is over 16 MB                                       | Send recent messages after a `compactionSummary`                   |
| 429    | `RATE_LIMITED`              | Too many agents awake, requests queued, or subscribers                  | Retry after `Retry-After`                                          |
| 503    | `UNAVAILABLE`               | A node is draining or starting, or an agent is moving                   | Retry after `Retry-After`; nothing happened                        |
| 500    | `INTERNAL`                  | A bug                                                                   | Retry, and report it with the request id                           |

Common `CONFLICT` cases:

* **An existing agent's subject, context, definition or mounts can't change.**
  An upsert of an existing key changed a field that's set only at creation.
  Delete the agent or use another key.
* **This input is already answered.** Someone answered first. The body's
  `input` says how it settled. Answering again the same way is a 200.
* **The definition is at revision N, not M.** A definition update with a stale
  `revision`. Read it again and retry.

## Run failures

| `RunError.code`     | Meaning                                                                                                                                                                                                                                                          |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `model_key_missing` | No key is set for the agent's model. The error names the model and the remedy: set a key with `PUT /v1/providers/{provider}/key` (or the key scope's entry), or move the agent to a model you can use (`GET /v1/models?available=true`)                          |
| `model_key_invalid` | The provider refused the key (HTTP 401). The error says the same remedy, then the provider's own words                                                                                                                                                           |
| `model_error`       | The model provider refused or failed after 3 retries for transient failures: a bad request, refused content, or a context overflow compaction couldn't fix                                                                                                       |
| `output_missing`    | A run sent with `output` ended without the model calling `final_output`: it answered in prose twice, the second time after a reminder. `text` has what it said. Send the prompt again, make the instructions say what the answer is for, or use a stronger model |
| `output_invalid`    | (SDKs only) The output fit the JSON Schema camelRun checked, but not the zod or pydantic schema the SDK parsed it with                                                                                                                                           |
| `spend_limit`       | The agent's spend limit (or the account's cap) stopped the turn after the response that crossed it. Raise the limit and send the next message                                                                                                                    |
| `runtime_error`     | camelRun couldn't carry out the run: for example, the agent was deleted                                                                                                                                                                                          |

Over REST, an ended request record (`GET /v1/agents/{id}/requests/{requestId}`)
has `status` on top: `completed`, `input_required` or `failed`, as the SDKs' runs
say it. Its `state` says only whether it ended (`running`, `completed`), so a
failed run is `state: "completed"`, `status: "failed"`, with `error` saying why.

A run with `uncertain: true` was cut short by a restart where it couldn't
resume. Its tool calls may or may not have taken effect, so check before
retrying.

## Tool errors

A run lists each tool call that didn't complete as
`toolErrors: [{tool, toolCallId?, code, outcomeUnknown?, message}]`. The model
got the same message and carried on.

| `code`               | Meaning                                                              | Outcome unknown |
| -------------------- | -------------------------------------------------------------------- | --------------- |
| `timeout`            | No answer or progress within the tool's deadline                     | Yes             |
| `connection_lost`    | The connection closed before the answer arrived                      | Yes             |
| `not_connected`      | No process served the agent's attached tools, so the call didn't run | No              |
| `source_unavailable` | Its MCP server couldn't be reached, listed or authenticated with     | No              |
| `failed`             | Anything else that kept it from running                              | No              |

A tool that ran and reported its own failure (threw, or answered `isError`) is
not a tool error: that's the tool's answer, which the model reads.

## SDK errors

|                                        | TypeScript                                                                          | Python                                                                   |
| -------------------------------------- | ----------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| Any failure                            | `AgentError`: `message`, `status`, `code`, `requestId`, `uncertain`, `retryAfterMs` | `AgentError`: `status`, `code`, `request_id`, `uncertain`, `retry_after` |
| A failed run                           | `RunError extends AgentError`, with `run`                                           | `RunError`, with `run`                                                   |
| Another process took the agent's tools | `code: "APPLICATION_REPLACED"`, through `onError`                                   | The same, through `on_error`                                             |
| A served tool's token check failed     | `RuntimeTokenError`                                                                 | `RuntimeTokenError`                                                      |

A run's failure is not an HTTP response, so `RunError.status` is always 0, in
both SDKs: it does not mean the network failed. Branch on `code`
(`model_key_missing`, `spend_limit`, …). An `AgentError` with `status` 0 and no
`code` is the connection's failure.

Stopping a wait (`signal`, or Python `timeout`) doesn't stop the run. Wait for
it again with the same `idempotencyKey`.
