JuliusBrussee/caveman · error · Error
cave_subagent_framework_runner_required
Error message
cave_subagent_framework_runner_required
What it means
The subagent tool factory returns a ToolDefinition whose runtime is { kind: "subagent", ... }; a host framework that understands that runtime replaces execute(). The stub execute() that throws cave_subagent_framework_runner_required exists so that running the tool through a plain tool executor (which would call execute directly) fails loudly instead of silently doing nothing.
Source
Thrown at packages/agent/src/index.ts:204
}
return tool({
name: options.name,
description: options.description,
input: schema.object({ task: schema.string() }),
effect: "read",
result: "auto",
...(options.timeoutMs === undefined ? {} : { timeoutMs: options.timeoutMs }),
runtime: {
kind: "subagent",
definition: options.agent,
maxInputChars,
maxCalls,
maxCostUsd,
...(options.maxTokens === undefined ? {} : { maxTokens: options.maxTokens }),
maxContextTokens,
},
async execute() {
throw new Error("cave_subagent_framework_runner_required");
},
});
}
View on GitHub (pinned to 27d5a3981a)
Solutions
- Run the tool through the caveman agent runtime that handles runtime.kind === "subagent" instead of calling execute() yourself
- If you need a plain executable tool, supply your own execute in the options rather than relying on the factory stub
- In tests, mock at the runtime boundary (the framework runner), not at execute()
- Check that @caveman-ai host packages (agent runner) are version-matched with this primitives package
Example fix
// before
const def = subagentTool({ agent, name: "worker" });
const out = await def.execute({ task: "do it" }); // throws cave_subagent_framework_runner_required
// after
// Register the definition with the agent runtime; the framework installs the real runner
const runner = createAgentRunner({ tools: [subagentTool({ agent, name: "worker" })] });
const out = await runner.callTool("worker", { task: "do it" }); Defensive patterns
Strategy: type-guard
Validate before calling
const def = subagentTool({ agent, name: "worker" });
if (def.runtime?.kind === "subagent") {
// must be executed by the framework runner, never def.execute() directly
registerWithAgentRuntime([def]);
} else {
await def.execute(input); // safe only for plain tools
} Type guard
const hasSubagentRuntime = (
d: ToolDefinition,
): d is ToolDefinition & { runtime: { kind: "subagent" } } =>
(d as { runtime?: { kind?: string } }).runtime?.kind === "subagent"; Try / catch
try {
await def.execute(input);
} catch (e) {
if (e instanceof Error && e.message === "cave_subagent_framework_runner_required") {
throw new Error("subagent tools must run through the agent runtime, not execute()");
}
throw e;
} Prevention
- Never call execute() on tools you did not construct with your own execute
- Centralize tool dispatch in one place that checks runtime.kind first
- Version-lock the primitives and runner packages so runtime kinds stay understood
When it happens
Trigger: Registering the subagent tool with a generic tool-runner that invokes definition.execute() itself, rather than passing the ToolDefinition to the caveman agent runtime that recognizes the subagent runtime kind. Also triggered by test harnesses that call tool.execute() directly.
Common situations: Mixing primitives from this library into a custom executor, unit tests that stub the runtime layer and call execute, or upgrading the agent package while the host runner stays on an older version that predates subagent runtime support.
Related errors
- cave_subagent_invocation_limit
- cave_subagent_concurrency_limit
- cave_subagent_depth_limit
- cave_host_sandbox_nested_under_required
- caveman agent: subagent maxInputChars must be a positive int
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/c9278e87c68c6b05.
Report an issue: GitHub.