Error Handling
Errors can appear in four layers: the HTTP status, the standard envelope, the JSON-RPC error object, and the isError flag on an MCP tools/call result.
Check them in that order, then read the API's own fields: status and exit_code for commands, data.error_type for files, and outputs for code cells. Streaming is the exception: a WebSocket can report an error after the handshake succeeds.
Where a failure shows up
Different planes report failures differently, by design. An exit code of 1 means the command ran and failed; a missing file was never read successfully.
- A non-zero exit code is not a transport error. Running
exit 3atPOST /v2/commandsstill answers200,success: true,status: "completed", andexit_code: 3. If the wait expires while the command is still running, the answer isstatus: "running", not a timeout error. - A failed code cell returns
200andsuccess: false;data.statusis"error", and the traceback is inoutputs. - A missing session or command id answers
404on every execution API. WebSocket is different: the handshake still completes with101, then the socket receives{"type": "error", "data": "Session not found"}and closes. A watcher's SSE stream returns404before opening.
Response envelope
v1 and most v2 responses share one shape:
success: false means aiod understood the request, but the operation failed. Read message, and read hint when it is not null. This is not a transport error.
Validation errors
A request that fails schema validation answers 422, whatever the plane:
A query field uses the same validation. location is ["query", "path"] when the field is missing, and ["query"] alone for a type error. The deserializer gives the reason (for example, invalid digit found in string) but not the field name.
An absent request body reads as {}: a route whose fields are all optional accepts a call with no body, and one with a required field answers as above.
File errors
File-plane failures carry a structured data object, not a bare message:
The data object is the same on /v2/fs and /v1/file; only the outer HTTP status differs.
retryable distinguishes two kinds of failure. A transient one, such as a briefly held lock, may succeed on retry. A persistent one, such as a bad path, will fail again until its cause is fixed. The error_type to status mapping is in File (FS).
HTTP status conventions
These codes can have different causes depending on the route:
MCP tool errors
A tools/call that fails inside the tool still returns a normal JSON-RPC result, with isError set. This is not an HTTP error: the transport succeeded, but the tool did not.
isError includes:
- A missing or invalid argument, such as
sandbox_execute_bashwithoutcmd, which answerscmd is required. - Any file API failure; the structured error object arrives as the result text.
browser_get_infowith nothing on the CDP port, orbrowser_gui_screenshotandbrowser_gui_execute_actionwithout a running computer-use worker.- A call to a stopped
EXTRA_MCP_SERVERSupstream. Its tools are also absent fromtools/list.
A name that does not exist is a protocol error instead: /mcp answers 200 with a JSON-RPC error object, -32601 for an unknown method and -32602 for an unknown tool. A body that is not JSON is -32700.
Client pattern
The envelope and the plane's own outcome are separate checks. A client can handle success: false; the caller still reads the exit code and cell status:
The same three reads in TypeScript:
Retry retryable: true and 503; retry 429 after a pause. Retrying 400, 404, 409, or 422 unchanged is usually pointless because nothing on the daemon side will change.
Related
- File (FS) — the structured file error and its statuses
- Commands (Bash) — command lifecycle and exit codes
- Terminals (PTY) — session and stream failures
- Authentication — what answers
401