ErrLookup › Background articles › ToolError: Tool Execution Failed — What This Error Class Means and How to Fix It
ToolError: Tool Execution Failed — What This Error Class Means and How to Fix It
ToolError is an error class raised by agent frameworks and tool runtimes (such as oh-my-pi and OpenManus) when a tool call — a browser action, file read/write, session operation, or model-backed helper — cannot complete. This page explains the common mechanisms behind ToolError, the typical triggers (missing parameters, unsupported capabilities, changed or invalid session state, unreachable infrastructure), and the fixes and prevention habits that hold across the family.
Distilled from 422 documented records across 5 repositories.
Background
ToolError is not a single message from a single library. It is a catch-all error class that agent frameworks use on the boundary between an LLM-driven tool call and the thing the tool touches: a browser daemon, a sandbox container, a session file, a debug adapter, or a model registry. Because the class name is generic, the actual diagnostic lives in the message text, and each library fills that text with its own remediation hints — for example 'Set PUPPETEER_EXECUTABLE_PATH to use an existing Chrome/Chromium binary', 'Current adapter does not support disassembly', or 'command is required for custom_request'. Reading the embedded message is the first debugging step in every case.
Most ToolErrors come from three broad mechanisms. The first is parameter and capability validation: tools refuse calls before doing work when a required field is missing ('command is required for custom_request'), a selector matches nothing ('no window matches ...'), a file type is unsupported, or a debug adapter did not advertise the needed DAP capability. The second is state drift: the runtime holds cached records or registrations and finds the world changed underneath them — a vibe worker whose parent session file was switched or compacted before a turn, a merge conflict whose marker block was edited after registration, a checkpoint already consumed by a prior rewind, or a question session whose answering client disconnected. The third is infrastructure and resolution failure: a Chromium download fails, a browser relay endpoint is unreachable, a sandbox file read/write throws, or a model resolver cannot find any model matching a configured role or tier.
From the caller's side the error usually arrives as a rejected tool result delivered back into the agent loop rather than a stack trace in your own code. Many runtimes deliberately wrap low-level exceptions this way: OpenManus's sandbox file operators catch every exception from the sandbox client and re-raise as ToolError with the original message appended (discarding the traceback via 'from None'), and oh-my-pi converts image size/decode failures into ToolError so an actionable message reaches the model instead of a corrupt payload entering the transcript. Some messages are themselves generated summaries — a bulk conflict resolution where every file failed throws its multi-line per-file report as the error text, and a failed auto-backgrounded eval cell surfaces its final output as the error message. So the string you see is often the most (and sometimes the only) diagnostic available.
The family varies across libraries in strictness and shape. oh-my-pi (the largest contributor of records) uses ToolError pervasively with feature-detection and fail-closed security paths — e.g. refusing skill:// paths that resolve outside a plugin root, or gating language backends behind configuration flags like eval.jl and PI_JL. FoundationAgents/OpenManus uses it as a broad wrapper around sandbox operations with the original exception message embedded, and also as a dispatch fallback for unrecognized tool commands (one exact lowercase command set, which catches LLM-issued typos like 'delete_plan' or ' remove'). xai-org/grok-build raises it for session-level failures such as a user-question channel closing when the client disconnects. Because conventions differ — some messages embed remediation, some embed only the underlying exception string — fixes are always message-specific even though the prevention patterns repeat.
Common causes
- State changed between operations.A cached record, conflict registration, checkpoint, or parent-session reference no longer matches the current world: the session file was switched, compacted, or forked; the conflict marker block was edited or reformatted; the checkpoint was already consumed by an earlier rewind; or the answering client disconnected. Reread or re-register against current state before retrying.
- Missing or invalid parameters.Required fields are absent or mis-keyed ('command is required for custom_request'), commands or selectors don't match any allowed value or live target, glob characters are used where only exact paths are supported, or a path points to a directory instead of an executable file. The message usually names the offending value.
- Unsupported format or capability.The target lacks a feature the tool requires: an image format outside PNG/JPEG/GIF/WEBP, a browser eval returning a Promise to a synchronous surface, a debug adapter whose DAP capabilities omit disassembly or restart, or a tool action the adapter can't handle. Check advertised capabilities or supported types before calling.
- Unreachable or broken infrastructure.The backing service failed: no network or a blocked host when downloading Chromium, a browser relay endpoint not listening or its extension never connected, a sandbox client that cannot reach the sandbox runtime, or an empty screenshot payload from a version-mismatched daemon. Verify health (e.g. GET /json/version) and keep client/daemon versions in sync.
- Configuration gaps for model and backend resolution.Role-based model resolution fails when no configured role matches a registry entry: modelRoles.vision/default/smol/slow unset or typo'd, no provider with credentials so the registry is empty, or a language backend deliberately disabled by config (eval.jl false or PI_JL=0). Fix the setting or environment flag, or drop the explicit model/tier option.
- File and path problems in sandboxes.Sandbox reads/writes throw when the file doesn't exist inside the container, the parent directory is missing, the path escapes the writable scope, or the disk is full or read-only. Sandbox paths are container-internal — host paths and host-style assumptions fail. Pre-check existence and mkdir -p parents.
- Lifecycle ordering violations.Operations are issued in an order the runtime forbids: calling rewind twice per checkpoint, killing a record twice, spawning vibe workers while the parent session is being forked or compacted, or enabling vibe kill paths against a session manager lacking the required optional method. Serialize lifecycle mutations and honor single-shot semantics.
What usually fixes it
- Read the embedded message first: ToolError messages usually carry the real diagnostic — the wrapped exception string, the unsupported value, or a per-file failure report — and often name the exact remediation.
- Refresh your view of the world before retrying: reread files, re-enumerate windows or models, re-check session scope and capabilities, and re-register anything (conflicts, workers, checkpoints) that was captured earlier.
- Fix configuration rather than code when the message points at roles, tiers, backend flags, or missing parameters — modelRoles entries, environment overrides like PI_JL or PUPPETEER_EXECUTABLE_PATH, and the tool's documented parameter keys.
- Verify infrastructure health and version parity: confirm endpoints answer (curl the relay or sandbox runtime), ensure daemons/CLIs are in sync, restore disconnected clients, and pre-warm caches in CI or air-gapped environments.
- Prevent by validating before acting: feature-check optional capabilities, stat paths, confirm file types by content, keep session lifetimes and lifecycle operations serialized, and preflight-checks (exists(), capabilities snapshot, allowed-command normalization) in automated callers.
Go deeper
- Connection failures: ECONNREFUSED, ECONNRESET, and friends — why connections get refused, reset, or dropped.
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Documented occurrences
- Vibe tombstone recovery requires parent-session persistence.(can1357/oh-my-pi)
- Failed to install Chromium for puppeteer: ${(err as Error).message}. Set PUPPETEER_EXECUTABLE_PATH to use an existing Chrome/Chromium binary, or install one manually.(can1357/oh-my-pi)
- Vibe session "${record.id}" changed parent scope before its turn started.(can1357/oh-my-pi)
- Vibe parent session changed before the worker could start.(can1357/oh-my-pi)
- Failed to read {path} in sandbox: {str(e)}(FoundationAgents/OpenManus)
- Unable to resolve a model for inspect_image.(can1357/oh-my-pi)
- Checkpoint already completed; continue from the retained rewind report instead of calling rewind again.(can1357/oh-my-pi)
- completion() could not resolve a model for the "${finalTier}" tier. Configure modelRoles.${finalTier === "default" ? "default" : finalTier} or ensure a provider is available.(can1357/oh-my-pi)
- ${finalText} || Eval cell failed(can1357/oh-my-pi)
- Glob patterns are not supported for internal URLs: ${rawPath}(can1357/oh-my-pi)
- Conflict #${entry.id} no longer present in '${entry.displayPath}': the recorded marker block can't be located. The file changed since the conflict was registered — re-read it to re-register conflicts.(can1357/oh-my-pi)
- Unrecognized command: {command}. Allowed commands are: create, update, list, get, set_active, mark_step, delete(FoundationAgents/OpenManus)
- resultText(can1357/oh-my-pi)
- ${label} returned a Promise, but this surface evaluates synchronously and cannot await it — return a plain value (poll with waitForFunction for async state instead)(can1357/oh-my-pi)
- inspect_image ':img' only supports .svg and .svgz files. / inspect_image only supports PNG, JPEG, GIF, and WEBP files detected by file content.(can1357/oh-my-pi)
- tab.extract(${JSON.stringify(format)}) found no readable content on ${url}(can1357/oh-my-pi)
- error.message (ImageInputTooLargeError or InvalidImageDataError)(can1357/oh-my-pi)
- User question session ended unexpectedly (client may have disconnected)(xai-org/grok-build)
- skill:// path resolves outside the plugin root: ${url}(can1357/oh-my-pi)
- cmux browser screenshot response did not include png_base64(can1357/oh-my-pi)
…and 402 more across the corpus — use search.
Honest provenance: generated on 2026-08-31 from AI-assisted analysis of the linked records. See how records are made.