{"record":{"id":"27d86b8f225c6d8d","repo":"JuliusBrussee/caveman","slug":"cave-subagent-depth-limit-27d86b","errorCode":"cave_subagent_depth_limit","errorMessage":"cave_subagent_depth_limit","messagePattern":"cave_subagent_depth_limit","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/agent/src/runtime.ts","lineNumber":3883,"sourceCode":"  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\n    ? undefined\n    : parentMeter.denomination === \"usd\" ? runtime.maxCostUsd : runtime.maxTokens;\n  if (parentMeter !== undefined && walletAmount === undefined) {\n    throw new Error(\"cave_subagent_wallet_denomination_unavailable\");\n  }\n  const carve = parentMeter === undefined || walletAmount === undefined\n    ? undefined\n    : parentMeter.carve(walletAmount);\n  if (parentMeter !== undefined && carve === undefined) {\n    throw new Error(\"cave_subagent_wallet_unavailable\");\n  }\n  let releaseAdmission: (() => void) | undefined;\n  try {\n    releaseAdmission = admitSubagent(executionContext.invocationState);","sourceCodeStart":3865,"sourceCodeEnd":3901,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/packages/agent/src/runtime.ts#L3865-L3901","documentation":"Subagent nesting depth is capped at min(parentOptions.maxSubagentDepth ?? DEFAULT_SUBAGENT_DEPTH_LIMIT, ABSOLUTE_SUBAGENT_DEPTH_LIMIT) — the absolute ceiling is 8 and the default is 2. A subagent already at the deepest allowed level that tries to spawn another throws cave_subagent_depth_limit.","triggerScenarios":"A subagent spawning a subagent spawning a subagent past the configured depth; recursive decomposition where each level delegates the same unsolved problem downward; run options validating maxSubagentDepth above 8 still get clamped to 8.","commonSituations":"Recursive research or fix loops with no depth guard in prompts; maxSubagentDepth lowered for cost control while prompts still encourage delegation; agents passing their own delegation habit to children.","solutions":["Raise runOptions.maxSubagentDepth — noting the absolute cap of 8 still applies","Make deep agents solve the task in-run instead of delegating further","Tell subagents their depth budget in the prompt so they stop delegating near the cap"],"exampleFix":"// before\nrun(agent, { maxSubagentDepth: 2 });\n\n// after\nrun(agent, { maxSubagentDepth: 4 }); // still capped at ABSOLUTE_SUBAGENT_DEPTH_LIMIT (8)","handlingStrategy":"validation","validationCode":"const ABSOLUTE_DEPTH = 8;\nconst limit = Math.min(options.maxSubagentDepth ?? 2, ABSOLUTE_DEPTH);\nif (context.depth + 1 > limit) {\n  return { error: `subagent depth limit (${limit}) reached — complete the task in this run` };\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set maxSubagentDepth deliberately (default 2, absolute cap 8)","Include the remaining depth budget in subagent prompts so they stop delegating near the cap","Design delegation as breadth (siblings) rather than depth (children of children)"],"tags":["subagents","recursion","limits"],"backgroundTag":"recursion-depth-limit","analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}