JuliusBrussee/caveman · error
cave_subagent_runtime_missing
cave_subagent_runtime_missing
Error message
cave_subagent_runtime_missing
What it means
The runtime looks up toolDefinition.runtime and requires it to be a subagent-kind runtime descriptor before executing a subagent tool call. If runtime is missing entirely or has a kind other than "subagent", the tool definition is not wired as a subagent and execution cannot proceed, so this error is thrown. It indicates a tool-registration/definition defect rather than bad user input.
Solutions
- Ensure the tool definition is created via the library's subagent tool factory so runtime.kind === "subagent" is set
- Inspect toolDefinition.runtime before dispatch; only route tools with a subagent runtime into this path
- Upgrade/align library versions if tool definitions come from a plugin built against a different schema
- Verify the tool name resolves to the intended subagent definition, not a regular tool
Example fix
// before
defineTool({ name: "researcher", execute });
// after
defineTool({ name: "researcher", runtime: { kind: "subagent", maxInputChars: 100000, maxCalls: 5, maxTokens: 50000 }, execute }); Defensive patterns
Strategy: type-guard
Validate before calling
if (toolDefinition.runtime?.kind !== "subagent") {
throw new Error(toolDefinition.name + " is not registered as a subagent tool");
} Type guard
function isSubagentTool(t) {
return t.runtime?.kind === "subagent";
} Try / catch
try {
await dispatchSubagent({ task });
} catch (e) {
if (e.message === "cave_subagent_runtime_missing") {
throw new Error("Tool is not wired as a subagent; check its ToolDefinition");
} else throw e;
} Prevention
- Always build subagent tools with the library's factory helpers
- Assert runtime.kind === "subagent" at tool registration time in tests
- Pin plugin/SDK versions so definition schemas match
- Never hand-roll ToolDefinition objects for subagents
When it happens
Trigger: Invoking a tool whose ToolDefinition was created without a runtime field, or whose runtime.kind is not "subagent", through the subagent execution path.
Common situations: Hand-built tool definitions missing the runtime descriptor; a plugin/extension passing a normal tool into the subagent runner; schema/refactor renaming the runtime field; version mismatch where tool definitions created by an older factory lack runtime.
Understand the failure class
Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.
Related errors
- cave_subagent_wallet_denomination_unavailable
- aes cipher
- apiKey, baseURL, and agent are required
- ASGI context resolver or request bounds are invalid
- AutoGen tools and workbench are mutually exclusive
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/8bbd7a76df744dc4.
Report an issue: GitHub.
Appendix: source
Thrown at packages/agent/src/runtime.ts:3866
return Object.freeze({
traceId: parent.traceId,
spanId: randomBytes(8).toString("hex"),
parentSpanId: parent.spanId,
});
}
async function executeSubagent(
toolDefinition: ToolDefinition,
params: unknown,
signal: AbortSignal | undefined,
parentOptions: InternalRunOptions,
usage: NestedUsage,
executionContext: InternalExecutionContext,
parentMeter: BudgetMeter | undefined,
parentDeadlineAt: number | undefined,
): Promise<unknown> {
const runtime = toolDefinition.runtime;
if (runtime?.kind !== "subagent") throw new Error("cave_subagent_runtime_missing");
if (params === null || typeof params !== "object" || Array.isArray(params) ||
typeof (params as { task?: unknown }).task !== "string") {
throw new Error("cave_subagent_arguments_invalid");
}
const task = (params as { task: string }).task;
if (task.length > runtime.maxInputChars) throw new Error("cave_subagent_input_limit");
const calls = usage.calls.get(toolDefinition.name) ?? 0;
if (calls >= runtime.maxCalls) throw new Error("cave_subagent_call_budget");
// Reserve synchronously before any await so parallel Pi tool dispatch cannot
// pass the same maxCalls check twice.
usage.calls.set(toolDefinition.name, calls + 1);
const depth = executionContext.depth;
const depthLimit = Math.min(
parentOptions.maxSubagentDepth ?? DEFAULT_SUBAGENT_DEPTH_LIMIT,
ABSOLUTE_SUBAGENT_DEPTH_LIMIT,
);
if (depth + 1 > depthLimit) throw new Error("cave_subagent_depth_limit");
// The wallet is carved here, still synchronously, for the same reason: twoView on GitHub (pinned to 3ee70a1026)