{"record":{"id":"5d57d05d72587aff","repo":"JuliusBrussee/caveman","slug":"cave-subagent-depth-limit","errorCode":"cave_subagent_depth_limit","errorMessage":"cave_subagent_depth_limit","messagePattern":"cave_subagent_depth_limit","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/definition-graph.ts","lineNumber":22,"sourceCode":"  \"@caveman-ai/agent:tool-implementation-source\",\n);\n\nexport function validateAgentGraph(root: AgentDefinition): void {\n  // Memoize per inherited containment posture: the same child definition\n  // reached under a sandbox-required ancestor must be re-checked, not skipped.\n  const visited = [new Set<AgentDefinition>(), new Set<AgentDefinition>()];\n  const active = new Set<AgentDefinition>();\n\n  const visit = (\n    definition: AgentDefinition,\n    depth: number,\n    sandboxRequired: boolean,\n  ): void => {\n    if (!definition || definition.kind !== \"agent\" ||\n        !Array.isArray(definition.tools)) {\n      throw new Error(\"cave_agent_definition_invalid\");\n    }\n    if (depth > 8) throw new Error(\"cave_subagent_depth_limit\");\n    if (active.has(definition)) throw new Error(\"cave_subagent_definition_cycle\");\n    // Host mode is an opt-in the root makes for itself. A descendant cannot use\n    // it to run closures outside an ancestor's required containment.\n    if (sandboxRequired && definition.sandbox === \"host\") {\n      throw new Error(\"cave_host_sandbox_nested_under_required\");\n    }\n    const memo = visited[sandboxRequired ? 1 : 0]!;\n    if (memo.has(definition)) return;\n    active.add(definition);\n    const childSandboxRequired = sandboxRequired ||\n      definition.sandbox === \"required\";\n    const names = new Set<string>();\n    for (const declared of definition.tools) {\n      if (!declared || declared.kind !== \"tool\" ||\n          typeof declared.name !== \"string\") {\n        throw new Error(\"cave_tool_definition_invalid\");\n      }\n      if (names.has(declared.name)) throw new Error(\"cave_duplicate_tool_name\");","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/definition-graph.ts#L4-L40","documentation":"The definition graph permits at most 8 levels of nested subagent definitions (visit is called with depth+1 for each subagent tool). Deeper chains are rejected to prevent runaway recursion and unbounded spawn trees, similar to agent-loop depth limits in other frameworks.","triggerScenarios":"Chaining 9+ agent definitions where each level's tool runtime references the next: A's tool has runtime.kind 'subagent' pointing to B, B's to C, and so on past depth 8.","commonSituations":"Auto-generated orchestrator hierarchies; recursive self-referencing definitions that unexpectedly traverse as a chain; delegation chains built by composition helpers that add a wrapper level per feature.","solutions":["Flatten the hierarchy: replace deep delegation chains with direct tool calls or a wider (not deeper) fan-out","If a level is only a pass-through wrapper, eliminate it and give its parent the child's tools directly","Instrument your builder to report the effective depth of generated graphs before registration"],"exampleFix":"// before: 9-level chain a1->a2->...->a9\nconst a9 = agent({ kind: \"agent\", tools: [...] });\n// ... each level wraps the next ...\n\n// after: flatten — a1 holds the leaf tools directly\nconst a1 = agent({ kind: \"agent\", tools: [leafTool1, leafTool2] });","handlingStrategy":"validation","validationCode":"function graphDepth(def: { tools: Array<{ runtime?: { kind?: string; definition?: unknown } }> }, seen = new Set()): number {\n  if (seen.has(def)) return 0; // cycle handled separately by the validator\n  seen.add(def);\n  let max = 0;\n  for (const t of def.tools) {\n    if (t.runtime?.kind === \"subagent\") {\n      max = Math.max(max, 1 + graphDepth(t.runtime.definition as never, new Set(seen)));\n    }\n  }\n  return max;\n}\nif (graphDepth(root) > 8) throw new Error(\"definition graph exceeds subagent depth limit of 8\");","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Compute the nesting depth of generated graphs before registering them","Prefer wide fan-out (many tools) over deep delegation chains","Eliminate pass-through wrapper agents that only add a depth level"],"tags":["subagent","recursion","depth-limit","graph"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}