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 === undefined

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. 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.
  2. Fix prompt/examples that demonstrate the tool with a different argument shape.
  3. 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

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


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/0105c4041466fce5. Report an issue: GitHub.