{"record":{"id":"0105c4041466fce5","repo":"JuliusBrussee/caveman","slug":"cave-subagent-arguments-invalid","errorCode":null,"errorMessage":"cave_subagent_arguments_invalid","messagePattern":"cave_subagent_arguments_invalid","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/runtime.ts","lineNumber":3869,"sourceCode":"    parentSpanId: parent.spanId,\n  });\n}\n\nasync function executeSubagent(\n  toolDefinition: ToolDefinition,\n  params: unknown,\n  signal: AbortSignal | undefined,\n  parentOptions: InternalRunOptions,\n  usage: NestedUsage,\n  executionContext: InternalExecutionContext,\n  parentMeter: BudgetMeter | undefined,\n  parentDeadlineAt: number | undefined,\n): Promise<unknown> {\n  const runtime = toolDefinition.runtime;\n  if (runtime?.kind !== \"subagent\") throw new Error(\"cave_subagent_runtime_missing\");\n  if (params === null || typeof params !== \"object\" || Array.isArray(params) ||\n      typeof (params as { task?: unknown }).task !== \"string\") {\n    throw new Error(\"cave_subagent_arguments_invalid\");\n  }\n  const task = (params as { task: string }).task;\n  if (task.length > runtime.maxInputChars) throw new Error(\"cave_subagent_input_limit\");\n  const calls = usage.calls.get(toolDefinition.name) ?? 0;\n  if (calls >= runtime.maxCalls) throw new Error(\"cave_subagent_call_budget\");\n  // Reserve synchronously before any await so parallel Pi tool dispatch cannot\n  // pass the same maxCalls check twice.\n  usage.calls.set(toolDefinition.name, calls + 1);\n  const depth = executionContext.depth;\n  const depthLimit = Math.min(\n    parentOptions.maxSubagentDepth ?? DEFAULT_SUBAGENT_DEPTH_LIMIT,\n    ABSOLUTE_SUBAGENT_DEPTH_LIMIT,\n  );\n  if (depth + 1 > depthLimit) throw new Error(\"cave_subagent_depth_limit\");\n  // The wallet is carved here, still synchronously, for the same reason: two\n  // subagents dispatched in one turn must not both be funded out of the same\n  // remaining budget.\n  const walletAmount = parentMeter === undefined","sourceCodeStart":3851,"sourceCodeEnd":3887,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/runtime.ts#L3851-L3887","documentation":"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.","triggerScenarios":"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 }).","commonSituations":"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.","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."],"exampleFix":"// before\nconst tool = subagent({ name: \"research\", input: z.any(), ... });\n\n// after\nconst tool = subagent({ name: \"research\", input: z.object({ task: z.string() }), ... });","handlingStrategy":"validation","validationCode":"function isValidSubagentParams(params: unknown): boolean {\n  return typeof params === \"object\" && params !== null && !Array.isArray(params) &&\n    typeof (params as { task?: unknown }).task === \"string\";\n}\nif (!isValidSubagentParams(candidateParams)) { /* reject before dispatch */ }","typeGuard":"function isSubagentParams(params: unknown): params is { task: string } {\n  return typeof params === \"object\" && params !== null && !Array.isArray(params) &&\n    typeof (params as { task?: unknown }).task === \"string\";\n}","tryCatchPattern":"try {\n  await dispatchSubagent(tool, params);\n} catch (error) {\n  if (error instanceof Error && error.message === \"cave_subagent_arguments_invalid\") {\n    // model output drifted: correct via tool schema + provider retry, not a blind retry\n  } else throw error;\n}","preventionTips":["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."],"tags":["subagent","arguments","validation","model-output"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}