ScalebrowserDOCS

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.

CodeHTTPWhat happened, and what to do
4001404Not found. The id does not exist. Do not retry it.
4002409The 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.
4003429This machine is full. Configured concurrency, configured memory budget and measured free memory all apply, and the strictest wins. Stop something here and retry.
4004503No engine for this profile's tier. Install one.
4005409The pre-launch check refused the start. The message names the reason, and retrying unchanged fails identically.
4006409Already running. Treat it as success, or stop the profile first.
4007n/aRetired and reserved. It belonged to a tier that no longer exists, and nothing returns it.
4008409Your plan's concurrent-browser allowance is used up, across every machine on the subscription. Release a browser anywhere, or move up a plan.
4010401The daemon's bearer token is missing, malformed or wrong. Re-read your token.
4011403No 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.
4012409This one profile is open on another machine of the same account. The message names it. Close it there, or take it over.
4013409The 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, 4005 is Coherence & proxies, 4008 is Plans & limits, 4011 and 4012 are Machines & sign-in, and 4013 is Proxies & exits.

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

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

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.

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?

FieldValuesMeaning
phaseprecondition · before_effect · effect · observationHow far the call got: refused up front, attempted but never sent, sent, or landed with only the reading afterwards failing.
effectnone · possible · confirmed · partialWhat it did to the world.
retryabletrue · falseWhether the agent may repeat the call without asking.

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:

CodeWhat happened
url_blockedThe address violates the open_page rule: not public http/https, or it resolves inward.
handle_requiredA profile-scoped call arrived without a handle.
handle_invalidThe handle is unknown, or belongs to another lease.
lease_expiredThe lease ran out or was released. Take a new one.
profile_leasedA management call targeted a profile somebody is actively working.
read_only_modeA mutating tool was called while the server runs read-only.
artifact_missingThe artifact id does not belong to this profile, or never existed.
artifact_quotaThe artifact store is full.
job_quotaThe background-job registry is full.
no_profile_availableNothing was free to lease.
concurrency_limitThe subscription's allowance is used up, across all machines. The REST twin is 4008.
profile_in_use_elsewhereThat profile is open on another machine. The REST twin is 4012.
secret_binding_refusedThe target does not match the stored value's binding.
secret_field_refusedThe field's kind does not match the value's kind.
secret_expiredThe stored value's own expiry has passed.
secret_unreadableThe value would not decrypt, or its row failed its integrity check.
secret_filter_unavailableThe reply was withheld because the secret filter could not run.
tool_failedAnything else. The message carries the underlying error.

before_effect, attempted and never sent to the browser:

CodeWhat happened
stale_refThe element reference belongs to a superseded document. A fresh full map rides with the refusal.
ambiguous_refThe reference resolves to more than one element.
click_interceptedSomething covers the click point.
element_offscreenThe element sits outside the window, so the point cannot be pressed.

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

CodeWhat happened
effect_unknownThe connection dropped mid-action.
partial_effectA multi-field action stopped partway through.
target_lostThe bound tab or the whole browser disappeared mid-action.
navigation_failedA history step or a reload did not complete.

observation, it landed and the reading afterwards failed:

CodeWhat happened
snapshot_failedTaking the map after a confirmed action failed.

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.

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: the surface these codes come out of.
  • Events: the failures that arrive as a stream rather than as a reply.
  • MCP server: where the phase and effect fields sit in a tool result.
  • Troubleshooting: what to do when the daemon itself is the problem.