JuliusBrussee/caveman · error · Error
cave_subagent_depth_limit
cave_subagent_depth_limit
Error message
cave_subagent_depth_limit
What it means
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.
Source
Thrown at packages/agent/src/definition-graph.ts:22
"@caveman-ai/agent:tool-implementation-source",
);
export function validateAgentGraph(root: AgentDefinition): void {
// Memoize per inherited containment posture: the same child definition
// reached under a sandbox-required ancestor must be re-checked, not skipped.
const visited = [new Set<AgentDefinition>(), new Set<AgentDefinition>()];
const active = new Set<AgentDefinition>();
const visit = (
definition: AgentDefinition,
depth: number,
sandboxRequired: boolean,
): void => {
if (!definition || definition.kind !== "agent" ||
!Array.isArray(definition.tools)) {
throw new Error("cave_agent_definition_invalid");
}
if (depth > 8) throw new Error("cave_subagent_depth_limit");
if (active.has(definition)) throw new Error("cave_subagent_definition_cycle");
// Host mode is an opt-in the root makes for itself. A descendant cannot use
// it to run closures outside an ancestor's required containment.
if (sandboxRequired && definition.sandbox === "host") {
throw new Error("cave_host_sandbox_nested_under_required");
}
const memo = visited[sandboxRequired ? 1 : 0]!;
if (memo.has(definition)) return;
active.add(definition);
const childSandboxRequired = sandboxRequired ||
definition.sandbox === "required";
const names = new Set<string>();
for (const declared of definition.tools) {
if (!declared || declared.kind !== "tool" ||
typeof declared.name !== "string") {
throw new Error("cave_tool_definition_invalid");
}
if (names.has(declared.name)) throw new Error("cave_duplicate_tool_name");View on GitHub (pinned to 27d5a3981a)
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
Example fix
// before: 9-level chain a1->a2->...->a9
const a9 = agent({ kind: "agent", tools: [...] });
// ... each level wraps the next ...
// after: flatten — a1 holds the leaf tools directly
const a1 = agent({ kind: "agent", tools: [leafTool1, leafTool2] }); Defensive patterns
Strategy: validation
Validate before calling
function graphDepth(def: { tools: Array<{ runtime?: { kind?: string; definition?: unknown } }> }, seen = new Set()): number {
if (seen.has(def)) return 0; // cycle handled separately by the validator
seen.add(def);
let max = 0;
for (const t of def.tools) {
if (t.runtime?.kind === "subagent") {
max = Math.max(max, 1 + graphDepth(t.runtime.definition as never, new Set(seen)));
}
}
return max;
}
if (graphDepth(root) > 8) throw new Error("definition graph exceeds subagent depth limit of 8"); Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- cave_sandbox_subagent_cycle
- cave_agent_definition_invalid
- cave_host_sandbox_nested_under_required
- caveman agent: subagent maxInputChars must be a positive int
- caveman agent: subagent maxCalls must be a positive integer
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/5d57d05d72587aff.
Report an issue: GitHub.