A code-review agent could spawn sub-agents to dive deeper into specific files. One sub-agent's prompt asked it to 'use the code review agent if needed.' It always thought it needed to. Each sub-agent spawned more sub-agents. By depth 12 there were 4,096 concurrent invocations.
What happened
Code-review pipeline. The top-level "review_pr" agent had access to a dive_into_file tool that spawned a sub-agent dedicated to one file. The sub-agent's tools included... dive_into_file (the spawning tool), recursively.
The sub-agent's system prompt:
You are reviewing one file. If the file references symbols defined in
other files, use dive_into_file to review those files for context.
For a 30-file PR with cross-imports, the sub-agent at depth 1 spawned 4 sub-sub-agents (one per imported file). Each of those spawned 4 more for their imports. Each of those, 4 more.
depth 0: 1 agent
depth 1: 4 agents
depth 2: 16 agents
depth 3: 64 agents
...
depth 6: 4,096 agents (when the orchestrator hit 100% CPU)
The orchestrator's own rate-limit machinery prevented total disaster — most of the depth-6 invocations got 429'd before they spawned children. But the bill from completed invocations: $312 for a single PR review.
Diagnosis
Three failures:
1. The recursive tool wasn't tracked across invocations. Each sub-agent thought it was the only one calling dive_into_file.
2. No depth limit. The system prompt encouraged recursion ("use it if needed") with no countervailing instruction ("but you are at depth N, do not recurse beyond depth 3").
3. Fan-out wasn't capped per parent. A sub-agent could spawn unlimited children.
The fix
class DiveToolFactory:
def __init__(self, max_depth=3, max_fanout=2):
self.max_depth = max_depth
self.max_fanout = max_fanout
def make_tool(self, current_depth: int, siblings_spawned: int):
if current_depth >= self.max_depth:
return None # Tool unavailable at this depth
async def dive_into_file(path: str):
if siblings_spawned >= self.max_fanout:
raise ToolCallLimitError(
f"Already spawned {self.max_fanout} sub-reviews. "
f"Summarize what you have."
)
return await run_subagent(
path,
tools=self.make_tool(current_depth + 1, 0),
)
return dive_into_file
System prompt addition:
You may dive into AT MOST 2 referenced files. After that, work with
what you have. The cost of recursive review compounds — be selective.
After fix: same 30-file PR reviews in 47 seconds, $0.18 cost.
Takeaway
If your agent has a tool that spawns more agents (which can include itself), you need: (a) a depth counter passed through the call graph, (b) a per-parent fan-out limit, (c) a global concurrent-invocation cap. All three. Recursive agents without these turn into self-DDoS in their first month of production.