JuliusBrussee/caveman · error
cave_sandbox_unknown_tool
cave_sandbox_unknown_tool
Error message
cave_sandbox_unknown_tool
What it means
At the end of the subagent path walk, the worker filters the final definition's tools for request.tool. If the filter does not yield exactly one match, or the single match is itself a subagent-runtime tool (which cannot be executed directly here), it throws `cave_sandbox_unknown_tool`. The resolved agent must own exactly one directly executable tool with the requested name.
Solutions
- Invoke the tool on the agent definition that actually declares it — add the tool to the leaf subagent if it should own it.
- Make tool names unique within each agent definition to eliminate the ambiguous multi-match case.
- Target a concrete executable tool; do not address a subagent delegation tool directly as the executed tool.
- Re-run the build after renaming tools so the parent's request reflects the current definitions.
Example fix
// before
const child = agent("child").tool(subagent("delegate", grandchild)).build();
await invoke(child, "search"); // search not on child
// after
const child = agent("child").tool(subagent("delegate", grandchild)).tool(search).build();
await invoke(child, "search"); Defensive patterns
Strategy: validation
Validate before calling
function toolIsDirectlyInvokable(def: AgentDefinition, name: string): boolean {
const matches = def.tools.filter((t) => t.name === name);
return matches.length === 1 && matches[0]!.runtime?.kind !== "subagent";
}
// assert toolIsDirectlyInvokable(leafDefinition, requestedTool) before invoking Type guard
const isExecutableTool = (t: unknown): t is { name: string; runtime?: { kind?: string } } =>
typeof t === "object" && t !== null &&
typeof (t as any).name === "string" &&
(t as any).runtime?.kind !== "subagent"; Try / catch
try {
await invokeTool(agent, toolName);
} catch (err) {
if (err instanceof Error && err.message.includes("cave_sandbox_unknown_tool")) {
console.error(`tool '${toolName}' is not a unique executable tool on the leaf agent`);
}
throw err;
} Prevention
- Invoke tools on the agent definition that actually declares them — tools are not inherited by subagents.
- Keep tool names unique within each agent definition.
- Never address a subagent delegation tool as the direct execution target.
- Re-run build after tool renames so parent requests match current definitions.
When it happens
Trigger: request.tool is not present on the final (leaf) agent definition, appears more than once (duplicate tool names), or names a subagent delegation tool instead of a concrete executable tool.
Common situations: Requesting a root-level tool by name on a subagent that does not have it (tools are not inherited down the graph); duplicate registration of the same tool name in the leaf agent; mistakenly targeting `invoke_agent`/a subagent tool directly; tool renamed after the parent hashed its digest.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- cave_sandbox_unknown_subagent
- azure Foundry api-version
- azure legacy inference path
- bedrock request path
- cave_sandbox_agent_export_missing
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/5ee36cfe291589c7.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/tool-worker.ts:127
throw new Error("cave_sandbox_definition_mismatch");
}
const visited = new Set<AgentDefinition>([definition]);
for (const name of request.agentPath) {
const delegated = definition.tools.filter((item) =>
item.name === name && item.runtime?.kind === "subagent"
);
if (delegated.length !== 1) throw new Error("cave_sandbox_unknown_subagent");
const child = delegated[0]!.runtime!.definition as AgentDefinition;
if (!child || child.kind !== "agent") {
throw new Error("cave_sandbox_subagent_definition_invalid");
}
if (visited.has(child)) throw new Error("cave_sandbox_subagent_cycle");
visited.add(child);
definition = child;
}
const selectedTools = definition.tools.filter((item) => item.name === request.tool);
if (selectedTools.length !== 1 || selectedTools[0]!.runtime?.kind === "subagent") {
throw new Error("cave_sandbox_unknown_tool");
}
const selected = selectedTools[0]!;
if (toolDefinitionSHA256(selected) !== request.toolDefinitionSha256) {
throw new Error("cave_sandbox_tool_definition_mismatch");
}
if (selected.effect !== "read" && request.allowSideEffects !== true) {
throw new Error("cave_sandbox_side_effect_denied");
}
const value = await selected.execute(request.params as never, AbortSignal.timeout(selected.timeoutMs));
writeResult({ ok: true, value });
} catch (error) {
writeResult({ ok: false, code: failureCode(error) });
}
View on GitHub (pinned to 3ee70a1026)