JuliusBrussee/caveman · error
cave_subagent_arguments_invalid
Error message
cave_subagent_arguments_invalid
What it means
executeSubagent validates that the tool parameters are a non-null, non-array object with a string `task` property; anything else throws cave_subagent_arguments_invalid. Subagent tools take exactly one argument: { task: string }. The error usually means the model emitted tool arguments that do not match the tool's declared input schema.
Source
Thrown at packages/agent/src/runtime.ts:3869
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: two
// subagents dispatched in one turn must not both be funded out of the same
// remaining budget.
const walletAmount = parentMeter === undefinedView on GitHub (pinned to 27d5a3981a)
Solutions
- Make the subagent tool's input schema require task: z.string() (or the framework's string schema) so invalid arguments are rejected at the provider boundary with a retryable validation error.
- Fix prompt/examples that demonstrate the tool with a different argument shape.
- Log the raw params on failure to see exactly which shape the model produced.
Example fix
// before
const tool = subagent({ name: "research", input: z.any(), ... });
// after
const tool = subagent({ name: "research", input: z.object({ task: z.string() }), ... }); Defensive patterns
Strategy: validation
Validate before calling
function isValidSubagentParams(params: unknown): boolean {
return typeof params === "object" && params !== null && !Array.isArray(params) &&
typeof (params as { task?: unknown }).task === "string";
}
if (!isValidSubagentParams(candidateParams)) { /* reject before dispatch */ } Type guard
function isSubagentParams(params: unknown): params is { task: string } {
return typeof params === "object" && params !== null && !Array.isArray(params) &&
typeof (params as { task?: unknown }).task === "string";
} Try / catch
try {
await dispatchSubagent(tool, params);
} catch (error) {
if (error instanceof Error && error.message === "cave_subagent_arguments_invalid") {
// model output drifted: correct via tool schema + provider retry, not a blind retry
} else throw error;
} Prevention
- Declare input: z.object({ task: z.string() }) (or equivalent strict schema) on every subagent tool.
- Keep prompt examples showing exactly { "task": "..." }.
- Treat occurrences as model-output drift: tighten the schema rather than catching and retrying the same args.
When it happens
Trigger: The model calls a subagent tool with params that are an array, a string, null, or an object whose `task` is missing/non-string (e.g. { tasks: [...] } or { task: 123 }).
Common situations: The tool's input schema does not enforce `task` as a required string, so the provider's JSON arguments drift; few-shot examples in the prompt show a different argument name; an adapter forwards positional args as an array.
Related errors
- caveman agent: subagent maxInputChars must be a positive int
- caveman agent: subagent maxCalls must be a positive integer
- caveman agent: subagent maxCostUsd must be positive
- caveman agent: subagent maxTokens must be a positive integer
- caveman agent: subagent maxContextTokens must be a positive
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/0105c4041466fce5.
Report an issue: GitHub.