---
title: "Errors"
description: "Every failure a client has to handle: the numeric REST codes with their HTTP status and remedy, and the MCP tool faults with the phase, effect and retryable fields that say whether an action landed."
canonical: "https://scalebrowser.net/docs/errors"
---

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

# Errors

> Every failure a client has to handle: the numeric REST codes with their HTTP status and remedy, and the MCP tool faults with the phase, effect and retryable fields that say whether an action landed.

Two error surfaces, deliberately different in shape. The REST API answers with an HTTP status and a stable number. An MCP tool answers with a result the model can read, carrying three fields that say whether the action happened.

## The REST shape
Every failing `/v1` request answers with these two fields and nothing else:

```json
{ "code": 4005, "message": "preflight failed: engine tampering detected" }
```

`code` is a stable number for the failures you are expected to handle by kind, and `null` for the generic ones, where the message is the whole story. The numbers are append-only: none is ever reused, and a retired one stays reserved.

## REST error codes
Generated from the daemon's own error enum and its HTTP mapping, so this table cannot fall behind the code.

| Code   | HTTP  | What happened, and what to do                                                                                                                                     |
| ------ | ----- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `4001` | `404` | Not found. The id does not exist. Do not retry it.                                                                                                                |
| `4002` | `409` | The proxy or geo check refused the launch: the exit country did not match, or could not be determined. Re-check the proxy, or change `geo_mode`.                  |
| `4003` | `429` | This machine is full. Configured concurrency, configured memory budget and measured free memory all apply, and the strictest wins. Stop something here and retry. |
| `4004` | `503` | No engine for this profile's tier. Install one.                                                                                                                   |
| `4005` | `409` | The pre-launch check refused the start. The message names the reason, and retrying unchanged fails identically.                                                   |
| `4006` | `409` | Already running. Treat it as success, or stop the profile first.                                                                                                  |
| `4007` | n/a   | Retired and reserved. It belonged to a tier that no longer exists, and nothing returns it.                                                                        |
| `4008` | `409` | Your plan's concurrent-browser allowance is used up, across every machine on the subscription. Release a browser anywhere, or move up a plan.                     |
| `4010` | `401` | The daemon's bearer token is missing, malformed or wrong. Re-read your token.                                                                                     |
| `4011` | `403` | No account is connected to this machine, so nothing may run. Sign in. Deliberately not `401`: a client answering that throws away a token that is perfectly good. |
| `4012` | `409` | This one profile is open on another machine of the same account. The message names it. Close it there, or take it over.                                           |
| `4013` | `409` | The exit address this start drew belongs to another profile. Wait, draw again, or relax the rule.                                                                 |

Where to go next for the ones with a page of their own: `4004` is [The engine](/docs/engine), `4005` is [Coherence & proxies](/docs/coherence), `4008` is [Plans & limits](/docs/account/plans), `4011` and `4012` are [Machines & sign-in](/docs/account/machines), and `4013` is [Proxies & exits](/docs/proxies).

Four HTTP statuses carry no number at all, because there is nothing to branch on:

| HTTP  | When                                                                       |
| ----- | ---------------------------------------------------------------------------- |
| `400` | Invalid input. The message names the field.                                 |
| `409` | A conflict the codes above do not cover.                                    |
| `422` | The body parsed but could not be acted on.                                  |
| `500` | An internal error. This is the only one worth retrying blindly.             |

<Note>

**`4003` and `4008` look alike and mean opposite things.** `4003` says this machine is out of room, so waiting helps. `4008` says the subscription is out of room, so waiting does not help and releasing a browser on another machine does. A client that folds them together sends the customer to the wrong place.

</Note>

## The MCP shape
An MCP tool does not fail with a protocol error. It answers with a result the model can see and reason about, carrying a code, a sentence and three fields that answer the only question that matters after a failed action: *did it happen?*

| Field       | Values                                                       | Meaning                                                                                                                       |
| ----------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- |
| `phase`     | `precondition` · `before_effect` · `effect` · `observation`  | How far the call got: refused up front, attempted but never sent, sent, or landed with only the reading afterwards failing.    |
| `effect`    | `none` · `possible` · `confirmed` · `partial`                | What it did to the world.                                                                                                     |
| `retryable` | `true` · `false`                                             | Whether the agent may repeat the call without asking.                                                                          |

<Warning>

**`retryable` is derived, not set.** It is true only when the effect is provably `none` or `confirmed`. `possible` and `partial` are exactly the cases where a retry posts twice, and there is no field an implementation could get wrong. When `retryable` is false, take a snapshot and look before acting again.

</Warning>

## MCP fault codes
Grouped by the phase they always belong to, because the phase decides what you do about them. Generated from the daemon's fault taxonomy.

**`precondition`, nothing was attempted:**

| Code                        | What happened                                                                                |
| --------------------------- | -------------------------------------------------------------------------------------------- |
| `url_blocked`               | The address violates the `open_page` rule: not public `http`/`https`, or it resolves inward. |
| `handle_required`           | A profile-scoped call arrived without a handle.                                              |
| `handle_invalid`            | The handle is unknown, or belongs to another lease.                                          |
| `lease_expired`             | The lease ran out or was released. Take a new one.                                           |
| `profile_leased`            | A management call targeted a profile somebody is actively working.                           |
| `read_only_mode`            | A mutating tool was called while the server runs read-only.                                  |
| `artifact_missing`          | The artifact id does not belong to this profile, or never existed.                           |
| `artifact_quota`            | The artifact store is full.                                                                  |
| `job_quota`                 | The background-job registry is full.                                                         |
| `no_profile_available`      | Nothing was free to lease.                                                                   |
| `concurrency_limit`         | The subscription's allowance is used up, across all machines. The REST twin is `4008`.       |
| `profile_in_use_elsewhere`  | That profile is open on another machine. The REST twin is `4012`.                            |
| `secret_binding_refused`    | The target does not match the stored value's binding.                                        |
| `secret_field_refused`      | The field's kind does not match the value's kind.                                            |
| `secret_expired`            | The stored value's own expiry has passed.                                                    |
| `secret_unreadable`         | The value would not decrypt, or its row failed its integrity check.                          |
| `secret_filter_unavailable` | The reply was withheld because the secret filter could not run.                              |
| `tool_failed`               | Anything else. The message carries the underlying error.                                     |

**`before_effect`, attempted and never sent to the browser:**

| Code                | What happened                                                                                    |
| ------------------- | ------------------------------------------------------------------------------------------------ |
| `stale_ref`         | The element reference belongs to a superseded document. A fresh full map rides with the refusal. |
| `ambiguous_ref`     | The reference resolves to more than one element.                                                 |
| `click_intercepted` | Something covers the click point.                                                                |
| `element_offscreen` | The element sits outside the window, so the point cannot be pressed.                             |

**`effect`, something was sent and the outcome is not fully known:**

| Code                | What happened                                              |
| ------------------- | ---------------------------------------------------------- |
| `effect_unknown`    | The connection dropped mid-action.                         |
| `partial_effect`    | A multi-field action stopped partway through.              |
| `target_lost`       | The bound tab or the whole browser disappeared mid-action. |
| `navigation_failed` | A history step or a reload did not complete.               |

**`observation`, it landed and the reading afterwards failed:**

| Code              | What happened                                   |
| ----------------- | ----------------------------------------------- |
| `snapshot_failed` | Taking the map after a confirmed action failed. |

<Note>

**Every secret refusal is a `precondition`.** That is a timing rule, not a coincidence: a value is resolved before the first keystroke, so a refusal cannot leave half a password in a field. It also keeps the pause the humanization layer calibrated from being moved by a round trip. [Secrets](/docs/secrets).

</Note>

## An unknown argument is an error
Every published tool schema forbids extra properties, so a misspelled argument name comes back as a refused call rather than being dropped.

The alternative was measured and rejected. A silently ignored argument produced an unchanged page, which reads exactly like a page that will not move, and the agent then spends its turns on the wrong problem.

## Next
- [REST API](/docs/rest): the surface these codes come out of.
- [Events](/docs/events): the failures that arrive as a stream rather than as a reply.
- [MCP server](/docs/agents/mcp-server): where the phase and effect fields sit in a tool result.
- [Troubleshooting](/docs/ops/troubleshooting): what to do when the daemon itself is the problem.
