Error codes
Every tool failure is one JSON envelope with a stable
errorcode. This page lists the codes and what triggers each. Match onerror, never onmessage.
The envelope
Every failure — for every tool — is serialized as a single JSON object:
json
{ "error": "<code>", "message": "<human-readable description>" }Some codes add fields (for example, a failing multi_edit reports the failing edit index in its message). The error value is one of the stable error codes; parse it, don't match on message text.
In DispatchResult terms: a failure sets isError: true and puts this JSON in the single text part of content (read it with contentText(content)). Success never uses the envelope — its content is the tool's output (or, for bash, a { exit_code, … } JSON object, which is not an error even on a non-zero exit).
Codes
| Code | Meaning | Raised by (examples) |
|---|---|---|
invalid_input | Arguments failed schema or semantic validation. | Any tool (ajv); edit_file identical strings; move/copy existing destination or symlink; remove symlink; outline/check_syntax unsupported extension; replace bad regex, empty-string-matching pattern, missing scope, or symlink target. |
not_found | Path does not exist — or the tool name is unknown/hidden. | All path tools; move/remove missing source; unknown, read-only-hidden, or tree-sitter-hidden tool. |
not_a_file | Path was a directory where a file was expected (or vice). | read_file, list_dir, tree, write_file, apply_patch, move, copy, mkdir, remove, bash (bad cwd). |
is_binary | File appears binary; text tools refuse it. | read_file, edit_file, multi_edit, apply_patch, outline, check_syntax, diff (read_files reports it per entry). |
not_an_image | File is not a supported image format (PNG/JPEG/GIF/WebP). | read_image. |
no_match | old_string was not found. | edit_file, multi_edit. |
ambiguous_match | old_string matched more than once without replace_all. | edit_file, multi_edit. |
patch_failed | A patch hunk did not apply cleanly (names the file). | apply_patch. |
timeout | The command exceeded its timeout (partial output returned), or a parse exceeded its budget. | bash; outline/check_syntax. |
output_limit | Output exceeded the hard capture ceiling; the command was killed. | bash. |
aborted | The run was cancelled via the dispatch AbortSignal; for bash the process group was SIGKILLed. | bash; outline/check_syntax. |
too_large | Input file exceeded maxFileBytes (or the 2 MB parse limit). | read_file, edit_file, multi_edit, apply_patch, outline, check_syntax, diff. |
path_escape | Path resolved outside the confined workspace root. | Any path tool when confineToWorkspace is on. |
monitor_not_found | No monitor has the given id (it never existed or was stopped). | monitor_poll, monitor_stop. |
too_many_monitors | monitor_start would exceed maxMonitors live monitors. | monitor_start. |
io_error | An underlying filesystem or process error. | Any tool; bash spawn failure. |
internal | Unexpected internal error (details logged to stderr, not returned). | Any tool. |
Handling them
- Unknown / hidden tool →
not_found. Calling a tool that isn't registered — including a mutating tool whilereadOnlyis set — is anot_founderror, not a thrown exception. ambiguous_match/no_matchare recoverable. Feed the envelope back to the model so it can widen or narrowold_stringand retry. Seeedit_file.internalnever leaks details.serializeErrorlogs the stack to stderr and returns a generic{ "error": "internal", "message": "internal error" }— safe to surface to a caller.
See also
- Core API → Errors —
ToolError,serializeError,fsError,ErrorCode - The tools — which codes each tool can return
- createAgentTools → DispatchResult — how errors reach you