JuliusBrussee/caveman · error
cave_sandbox_unknown_tool
cave_sandbox_unknown_tool
Error message
cave_sandbox_unknown_tool
What it means
After descending agentPath, the worker looks up request.tool in the final definition's tools. It requires exactly one match whose runtime.kind is NOT "subagent" (you cannot invoke a subagent slot directly as a tool). Zero matches, multiple matches, or a match that is itself a subagent all produce this error.
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 27d5a3981a)
Solutions
- Confirm request.tool matches exactly one non-subagent tool name in the agent that agentPath resolves to.
- Fix agentPath so it terminates at the agent that actually owns the tool.
- To run a subagent's tool, extend agentPath with the subagent name and target its inner tool; never point request.tool at a subagent slot.
Example fix
// before: tool "search" lives inside subagent "research"
{ agentPath: [], tool: "search" } // cave_sandbox_unknown_tool
// after
{ agentPath: ["research"], tool: "search" } Defensive patterns
Strategy: validation
Validate before calling
function toolResolves(def: AgentDefinition, toolName: string): boolean {
const matches = def.tools.filter((t) => t.name === toolName);
return matches.length === 1 && matches[0].runtime?.kind !== "subagent";
} Type guard
function isExecutableTool(t: { runtime?: { kind?: string } }): boolean {
return t.runtime?.kind !== "subagent";
} Prevention
- Validate (agentPath, tool) pairs against the current definition before enqueueing worker requests.
- Never point the tool field at a subagent slot; extend agentPath instead.
When it happens
Trigger: request.tool names a nonexistent tool in the resolved agent; the name resolves only to a subagent-runtime entry; two tools share the name; the tool lives on a different agent in the path than agentPath reached.
Common situations: Tool renamed or removed while a queued worker request still names the old identifier; agentPath too short/long so resolution lands on the wrong agent; invoking a subagent by its tool name instead of going through normal delegation.
Related errors
- cave_sandbox_agent_export_missing
- cave_sandbox_definition_mismatch
- cave_sandbox_unknown_subagent
- cave_sandbox_subagent_definition_invalid
- cave_sandbox_subagent_cycle
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/5ee36cfe291589c7.
Report an issue: GitHub.