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

# Errors

> Stable, machine-readable error codes on both planes: branch on the code, show the message.

Every error uses a standard HTTP status code and returns a stable, machine-readable code in a JSON `error` field: an object with a `code` on both API catalogs, a flat string on transport failures between you and the gateway. Branch on the code, never on `message` or the HTTP status alone. There are two catalogs because there are two planes, plus a short list of transport errors.

<Info>
  Both planes take the same `sk_live_` key, as `Authorization: Bearer` on the hosting API and as `X-Agent37-Key` on instance URLs, but their error envelopes differ: the Agent API adds optional `param`, `hint`, and `response_id` fields. See [Core concepts](/docs/agents-api/concepts) for the two planes.
</Info>

## Hosting API errors

Errors from `https://api.agent37.com/v1/*` always carry `code` and `message`, plus `detail` on the one error that has raw output worth showing: a failed create's `provisioning_failed` includes the tail of your container's console log, so a container that crashed on boot returns its own startup error. There is no `param` or `hint` on this plane.

```json theme={null}
{
  "error": {
    "code": "insufficient_balance",
    "message": "This instance costs $0.0068 per hour, metered per minute; creating it requires at least one day of balance ($0.1624). Add balance to your workspace and try again."
  }
}
```

<ResponseField name="error.code" type="string">
  A stable, machine-readable identifier. Branch on this.
</ResponseField>

<ResponseField name="error.message" type="string">
  A human-readable description. Safe to show, but do not parse it.
</ResponseField>

<ResponseField name="error.detail" type="string">
  Optional. Raw failure output, present on a failed create's `provisioning_failed`: the last lines of your container's console log (host paths and IPs redacted, truncated to the final 2,000 characters). The console log is appended across boots, so lines from an earlier boot can appear above the failing boot's lines; read from the bottom. The same text persists on the instance as [`status_reason.detail`](/docs/agents-api/instances#the-instance-object).
</ResponseField>

### Hosting API codes

| Code                      | HTTP    | When                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| ------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `invalid_api_key`         | 401     | The `sk_live_` Bearer key is missing, malformed, or revoked, on any `/v1` path.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `ip_not_allowed`          | 403     | The key is valid but the request came from an IP outside the key's [IP allowlist](/docs/agents-api/concepts#two-planes-one-key). The `message` includes the observed IP.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| `invalid_request`         | 400     | A request field is invalid: bad JSON, an unsupported resource shape, a direct image ref where a template name belongs, a [template](/docs/agents-api/templates) create with no `image_ref`, a registry reference the platform cannot pull (private without `registry_auth`, or a wrong login), starting a [template build](/docs/agents-api/templates#build-an-image-in-the-cloud) whose context is missing or over 1 GB or whose `secrets` or `registry_auth` body is malformed, a lifecycle action in the wrong state, `stop` on a sleeping instance parked in cold storage (wake it with `start` or delete it), an invalid [`update`](/docs/agents-api/instances#update) body (any field other than `template`, or a bad or unpublished [version pin](/docs/agents-api/templates#pin-a-template-version)), a [`restore`](/docs/agents-api/instances#restore) with a malformed body, a field other than `backup`, or on an instance that is not `running` or `stopped`.                             |
| `image_too_large`         | 400     | A [template](/docs/agents-api/templates)'s staged image is over the 8 GB decimal cap (`8,000,000,000` bytes).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `forbidden`               | 403     | A write to a system template. System templates are read-only.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `not_found`               | 404     | No instance, template, or [backup](/docs/agents-api/instances#list-backups) with that id or name in your workspace, or an unknown `/v1` path.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `insufficient_balance`    | 402     | The wallet holds less than a day of the instance's running rate at create, or is negative at `start` of a `past_due` instance.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| `instance_limit_reached`  | 409     | The workspace is at its instance limit: one instance on the free credit, 10 once you have topped up, 50 once top-ups total \$100, 200 once they total \$250. Email [vishnu@agent37.com](mailto:vishnu@agent37.com) to raise it further.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `tier_limit`              | 403     | Create or resize asks for a shape larger than your plan includes, or a [custom domain](/docs/agents-api/domains) is registered before your top-ups total \$100. Free workspaces (before your first top-up) run any template on the 2 vCPU / 4 GB shape; a top-up unlocks the 4/8 and 8/16 shapes.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `performance_unavailable` | 403     | [Create](/docs/agents-api/instances#create-an-instance) asks for `"type": "performance"` on a workspace it isn't enabled for. Nothing is provisioned and nothing is charged; book the call in the message.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `capacity_unavailable`    | 409     | `start` or `resize`: no host in the fleet can fit this instance, or the resize increase, right now. Also a wake (`start` or `exec` on a sleeping instance) that has to move the instance to another host when none has room.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `template_conflict`       | 409     | A template create or rename targets a name that already exists.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `build_conflict`          | 409     | A [template build](/docs/agents-api/templates#build-an-image-in-the-cloud) `start` on a build that already started, or while three builds are already running in the workspace. Wait for one to finish and retry.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `domain_exists`           | 409     | A [custom domain](/docs/agents-api/domains) registration names a hostname already registered, by your workspace or any other.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `try_again`               | 409     | A transient state conflict: the instance is mid-transition, for example `stop`, `start` or `exec` while a sleep checkpoint is being written, which takes tens of seconds on a large instance. Retry with backoff.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `rate_limited`            | 429     | An on-demand [backup](/docs/agents-api/instances#back-up-now) inside the 15-minute per-instance window, or while another backup is running on the same host. `Retry-After` says how many seconds to wait.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `no_capacity`             | 503     | Create only: no host can fit the requested shape right now. Safe to retry.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `image_ingest_failed`     | 502     | Registering a [template](/docs/agents-api/templates) failed while copying the image into private storage, for a reason on our side (not a bad image). Retry, or contact [support@agent37.com](mailto:support@agent37.com).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `provisioning_failed`     | 502/500 | A container or host operation failed (image pull, container start, lifecycle action), including a wake via `start` or `exec` that failed; after a failed wake the instance reads `sleeping` again, and after a failed [restore](/docs/agents-api/instances#restore) it reads `stopped`, or `running` when the restore never took the container down, with the reason in `status_reason` either way. A failed [restart](/docs/agents-api/instances#restart) likewise leaves the instance `running` unless its container is confirmed down, and an [update](/docs/agents-api/instances#update) that never reached the instance's host leaves a running instance `running`. A [backup](/docs/agents-api/instances#list-backups) list while backup storage is unreachable fails the same way rather than returning an empty list, and the instance is untouched. A failed create carries `error.detail` with your container's console tail. Creates debit nothing, so a failed create costs nothing. |

<Note>
  Lookups are uniform: an id that belongs to another workspace returns the same 404 as an id that does not exist, and unknown `/v1` paths 404 only after your key is validated. Nothing about other workspaces leaks through error responses.
</Note>

A [template build](/docs/agents-api/templates#build-an-image-in-the-cloud) that fails after starting is not an HTTP error: `GET /v1/template-builds/{id}` returns `200` with `status: "failed"` and the reason (`build_failed`, `build_timeout`, `invalid_request`, `invalid_image_archive`, `image_too_large`, `image_ingest_failed`, or `internal_error`) in its `error` field.

## Agent API errors

Errors from the gateway at `https://{instanceId}.agent37.app/v1/*` use the same envelope plus optional `param`, `hint`, and `response_id`.

```json theme={null}
{
  "error": {
    "code": "validation_error",
    "message": "goal mode is not yet supported on this gateway.",
    "param": "mode",
    "hint": "Use mode \"chat\"."
  }
}
```

<ResponseField name="error.param" type="string">
  The request field that was invalid. Present on `validation_error` when a specific field is at fault; a malformed JSON body has no `param`.
</ResponseField>

<ResponseField name="error.hint" type="string">
  A suggested next step, when one applies.
</ResponseField>

<ResponseField name="error.response_id" type="string">
  On `session_busy`: the id of the response already running on the session. Reattach with `GET /v1/responses/{id}/stream` or cancel it. Every `session_busy` from a current gateway carries it; treat it as optional only for instances still on an older gateway.
</ResponseField>

### Transport errors

Auth, instance lookup, and routing happen on the platform between you and the gateway, and rejections there use a flat string instead of the envelope: `{"error": "<code>"}`. Some carry a human-readable `message` (and, on a 401 with no credentials, a `docs` link); branch on the code, not on either. Check whether `error` is a string before reading `code`.

| Code                    | HTTP | When                                                                                                                                                                                                                                                                                                                                                    |
| ----------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `invalid_api_key`       | 401  | No credential, or a key or signed URL that did not verify. The `message` says which, and how to authenticate a browser request.                                                                                                                                                                                                                         |
| `ip_not_allowed`        | 403  | The key is valid but the request came from an IP outside the key's [IP allowlist](/docs/agents-api/concepts#two-planes-one-key). The `message` includes the observed IP. Signed URLs are unaffected.                                                                                                                                                         |
| `instance_suspended`    | 402  | The instance is suspended for non-payment, so the request will not wake it. Top up the workspace wallet and retry.                                                                                                                                                                                                                                      |
| `not_found`             | 404  | An instance URL that does not route: unknown, deleted, `failed`, or another workspace's.                                                                                                                                                                                                                                                                |
| `container_unavailable` | 502  | The instance is not running, typically `stopped`. Start it and retry.                                                                                                                                                                                                                                                                                   |
| `container_unreachable` | 502  | The instance is up but its service did not answer on the routed port, which is common briefly during a restart or update. Transient; retry.                                                                                                                                                                                                             |
| `upstream_unreachable`  | 502  | The platform could not reach the instance's host. Transient; retry.                                                                                                                                                                                                                                                                                     |
| `instance_saturated`    | 503  | A flood of simultaneous connections to one instance, far beyond what one user's agent produces. The instance is protected and your other instances are unaffected; retry in a moment.                                                                                                                                                                   |
| `host_mesh_not_ready`   | 503  | The instance's host is still joining the platform network. Transient; retry.                                                                                                                                                                                                                                                                            |
| `wake_timeout`          | 503  | A [wake](/docs/agents-api/urls#sleeping-instances-wake-on-request) of a sleeping instance did not finish within about 3 minutes. It keeps waking in the background; retry.                                                                                                                                                                                   |
| `wake_failed`           | 503  | A wake of a sleeping instance failed. It stays `sleeping`, and the next request tries again.                                                                                                                                                                                                                                                            |
| `upstream_timeout`      | 504  | The instance went silent for 15 minutes, either before the response started or mid-stream (see [Long requests](/docs/agents-api/urls#long-requests)). Agent turns have no time limit of their own, since the gateway answers immediately and keeps the stream alive. The work keeps running inside the instance: retry, or read the result from its session. |
| `internal_error`        | 500  | Unexpected platform error.                                                                                                                                                                                                                                                                                                                              |

### Agent API codes

| Code                 | HTTP | When                                                                                                                              |
| -------------------- | ---- | --------------------------------------------------------------------------------------------------------------------------------- |
| `validation_error`   | 400  | A request field is invalid (`param` names it), the body is not valid JSON, or `mode` is `"goal"` (reserved).                      |
| `not_a_directory`    | 400  | `GET /v1/files?path=` or `GET /v1/files/archive?path=` points at something that isn't a directory.                                |
| `response_not_found` | 404  | No response with that id.                                                                                                         |
| `file_not_found`     | 404  | No file or directory at that path (`GET /v1/files`, `GET /v1/files/archive`, `GET /v1/files/content`, `PATCH /v1/files`).         |
| `not_found`          | 404  | Unknown route on the instance URL.                                                                                                |
| `rename_unsupported` | 405  | `PATCH /v1/sessions/{id}` against a harness that cannot rename sessions (no native editable title).                               |
| `session_busy`       | 409  | A response is already running on this session; `error.response_id` names it (absent only on instances still on an older gateway). |
| `title_conflict`     | 409  | `PATCH /v1/sessions/{id}`: the requested title is already used by another session.                                                |
| `file_exists`        | 409  | `PUT /v1/files/content` with `overwrite=false` when a file already exists at the path.                                            |
| `modified`           | 412  | `PUT /v1/files/content` with `X-Expected-Mtime` when the file changed since you read it (lost-update guard).                      |
| `payload_too_large`  | 413  | A JSON request body exceeds 2 MB. The raw-body `PUT /v1/files/content` write is exempt.                                           |
| `rate_limited`       | 429  | An upstream provider rate limit. Back off and retry.                                                                              |
| `agent_error`        | 502  | The agent backend failed without a more specific code.                                                                            |
| `agent_unavailable`  | 503  | The targeted harness is not available on this instance: not provisioned here, or down.                                            |
| `internal_error`     | 500  | Unexpected gateway error.                                                                                                         |

<Note>
  The Agent API catalog is open-ended past this table. Failures inside the agent can surface provider-specific codes at 502 or 503 (for example a provider auth or quota error passes its raw code through, and an agent that is still warming up returns 503 with its own code). On OpenClaw a turn that ends in error carries OpenClaw's own error kind as `error.code` (`refusal`, `timeout`, `context_length`, or `unknown`); a provider rate limit arrives as `rate_limited`, and when OpenClaw reports no kind the code is `agent_error`. Treat any code you do not recognize as an agent-side failure: log it and show `message`.
</Note>

<Note>
  Not every failed turn is an HTTP error. `POST /v1/responses` never rejects because the agent failed: once the turn is accepted, the call returns 200 with `status: "failed"` and the same error object in the response body's `error` field, and streams end with a `response.failed` event. `rate_limited`, `agent_error`, `agent_unavailable`, and the provider-specific codes reach you this way on a turn, never as their HTTP status; the statuses in the table apply when the same failure hits a session or model call. Check `status`, not just the HTTP code. See [Send a message](/docs/agents-api/chat) and [Streaming](/docs/agents-api/streaming).
</Note>

<Note>
  The BYO-account harnesses signal a missing account with `auth_error`. Until an account is connected, a turn comes back `status: "failed"` with `error.code` `auth_error` and a `hint`, and `GET /v1/health` reports `"healthy": false`. On [Claude Code](/docs/agents-api/claude-code#connect-your-claude-account) the hint says to connect a Claude account; on [Codex](/docs/agents-api/codex#connect-your-openai-account) it says to run `codex login --device-auth` or set `OPENAI_API_KEY`; on [Grok](/docs/agents-api/grok) it says to set `XAI_API_KEY` or run `grok login --device-code`.
</Note>

## Handle them

Read `code`, then act by remedy: busy sessions get a cancel or a new session, transient codes get a retry with backoff, validation errors get fixed (read `param`), and anything unknown is agent-side.

<CodeGroup>
  ```python python theme={null}
  import requests

  r = requests.post(
      "https://ab12cd34ef.agent37.app/v1/responses",
      headers={
          "X-Agent37-Key": "sk_live_...",
          "Content-Type": "application/json",
      },
      json={
          "input": "Research the top 3 EV makers, write a memo.",
          "session_id": "7f3e0b6c52a949d2b1c4a8e9d0f31726",
      },
  )

  body = r.json()
  if not r.ok or body.get("status") == "failed":
      error = body["error"]
      if isinstance(error, str):
          raise RuntimeError(f"transport error: {error}")  # flat platform error, e.g. container_unavailable
      code = error["code"]
      if code == "session_busy":
          ...  # cancel the running response or start another session
      elif code == "rate_limited":
          ...  # transient: back off and retry
      elif code == "validation_error":
          raise ValueError(f"{error.get('param')}: {error['message']}")
      else:
          ...  # unknown codes are agent-side failures: log code, show message
  ```

  ```javascript node theme={null}
  const res = await fetch("https://ab12cd34ef.agent37.app/v1/responses", {
    method: "POST",
    headers: {
      "X-Agent37-Key": "sk_live_...",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      input: "Research the top 3 EV makers, write a memo.",
      session_id: "7f3e0b6c52a949d2b1c4a8e9d0f31726",
    }),
  });

  const body = await res.json();
  if (!res.ok || body.status === "failed") {
    const { error } = body;
    if (typeof error === "string") {
      // flat platform error, e.g. "container_unavailable": see the transport table
      throw new Error(`transport error: ${error}`);
    }
    switch (error.code) {
      case "session_busy":
        // cancel the running response or start another session
        break;
      case "rate_limited":
        // transient: back off and retry
        break;
      case "validation_error":
        throw new Error(`${error.param}: ${error.message}`);
      default:
        // unknown codes are agent-side failures: log code, show message
        console.error(error.code, error.message);
    }
  }
  ```
</CodeGroup>

The same grouping works on the Hosting API: `insufficient_balance` sends your user to the billing dashboard, `no_capacity` is retryable, and `invalid_request` means fix the request before retrying.

<Tip>
  `no_capacity` (503, hosting) and `rate_limited` (429, agent) are safe to retry with backoff. So are `try_again` (409, hosting) and a `provisioning_failed` create: creates debit nothing (the meter only starts once the instance runs), so retrying never double-bills.
</Tip>

## Codes worth a closer look

<AccordionGroup>
  <Accordion title="insufficient_balance (402): the workspace wallet is empty" icon="wallet">
    The workspace wallet cannot cover a charge. You see it in three places: at create, when the wallet holds less than one day of the instance's running rate (a check, not a debit; the meter only starts once the instance runs); at `start`, when a `past_due` instance's workspace balance is still negative; and inside agent behavior, when a managed call (LLM, Brave search, Composio) finds the wallet empty. That third case does not reach the Agent API as `insufficient_balance`: the refused LLM call fails the turn (`status: "failed"`, or a `response.failed` event) with `error.code` `quota_exhausted`, the same code a spent instance budget produces; the `message` names the workspace balance, so it tells the two apart. The fix is the same everywhere: top up the wallet at `https://www.agent37.com/dashboard/cloud/billing` (\$5 minimum, \$1000 max per top-up), and enable [automatic top-up](/docs/agents-api/billing#automatic-top-up) so it does not recur. See [Billing](/docs/agents-api/billing).
  </Accordion>

  <Accordion title="instance_budget_exhausted (402): this instance hit its budget" icon="gauge">
    The wallet has funds, but this instance has used up its own managed-spend budget: the monthly cap is consumed and no one-time top-up headroom remains. Only managed calls are refused; the instance keeps running and compute billing is unaffected. Raise the cap with `PATCH /v1/instances/{id}/budget` or add headroom with `POST /v1/instances/{id}/budget/top-up`. See [Budgets](/docs/agents-api/budgets). You will not see this code in an API error on either plane: it is what the managed endpoint returns to the agent's own call inside the instance. What you see is the failed turn (`status: "failed"`, or a `response.failed` event); on Hermes its `error.code` is `quota_exhausted`, with a hint naming the fix.
  </Accordion>

  <Accordion title="session_busy (409): one response at a time" icon="clock">
    A session runs one response at a time. Posting new input while a turn is in flight returns this, with the running response's id in `error.response_id`. Three ways out: reattach to the running turn with `GET /v1/responses/{response_id}/stream`, cancel it with `POST /v1/responses/{response_id}/cancel` (a finished response just returns its terminal state), or start a fresh session by omitting `session_id`. If the field is absent (an instance still on an older gateway), read `active_response_id` from `GET /v1/sessions/{id}` instead. See [Sessions](/docs/agents-api/sessions).
  </Accordion>

  <Accordion title="The capacity trio: instance_limit_reached, capacity_unavailable, no_capacity" icon="server">
    Three different walls, three different fixes. `instance_limit_reached` (409, create): the workspace is at its instance limit (one instance on the free credit, 10 once you have topped up, 50 once top-ups total \$100, 200 once they total \$250); delete instances you no longer need, or email [vishnu@agent37.com](mailto:vishnu@agent37.com) to raise the ceiling. `no_capacity` (503, create): no host can fit the requested shape right now; retry with backoff or pick a smaller shape, and a create that fails this way does not keep your money. `capacity_unavailable` (409, start or resize): the host that holds the instance's disk cannot fit it, and no other host has room to take it either (when the home host is full, the platform moves the instance to one with room before it ever fails); retry later. See [Instances](/docs/agents-api/instances).
  </Accordion>
</AccordionGroup>

<Note>
  Both 402 reasons are billing limits, not bugs. When a managed call is refused mid-turn, the refusal shows up in agent behavior (the turn fails or the agent reports it); the instance itself never goes down over managed spend.
</Note>
