TL;DR Claude Code no longer caps how many subagents a session can launch. If you want a brake, add it yourself: a
PreToolUsehook onAgentthat counts launches and blocks past the number you choose, andmaxTurnsin the frontmatter of every agent that launches others. Whatever you write in the prompt stops nothing.
Claude Code v2.1.212 introduced a cap of 200 subagents per session, and the changelog gave the reason: "to stop runaway delegation loops". Twelve versions later, v2.1.224 removed it. Two limits are left: 20 subagents running at once, and 3 levels of nesting. Neither counts the total. The docs put it plainly: "There's no limit on the total number of subagents Claude can spawn over a session".
Why a limit in the prompt doesn't work
I learned this with delegate, an agent in my craft plugin that builds a whole phase and launches reviewers along the way. Its prompt told it to stop at around 40 turns, counted by itself. In a real run it made 195 tool calls without stopping and launched 7 reviews for a single phase. Its final report said 4.
The model doesn't keep count of its own turns, and when it does, it gets the count wrong. A limit written into the prompt is a suggestion. One that Claude Code enforces is a limit. It's the same point as CLAUDE.md versus hooks: text is context, not a guarantee.
The total limit: a ten-line hook
A PreToolUse hook fires before every call to the Agent tool, which is how Claude launches subagents. This one counts the session's launches and, past the cap, blocks the next one.
1. The script, at ~/.claude/hooks/subagent-cap.sh
#!/bin/bash
LIMIT="${SUBAGENT_CAP:-30}"
sid=$(python3 -c 'import json, sys; print(json.load(sys.stdin)["session_id"])')
counter="${TMPDIR:-/tmp}/claude-subagents-$sid"
n=$(( $(cat "$counter" 2>/dev/null || echo 0) + 1 ))
echo "$n" > "$counter"
if [ "$n" -gt "$LIMIT" ]; then
echo "Subagent limit reached: $LIMIT per session. Do not launch more, finish with what you have." >&2
exit 2
fi
chmod +x ~/.claude/hooks/subagent-cap.sh
2. Register it in ~/.claude/settings.json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Agent",
"hooks": [
{ "type": "command", "command": "~/.claude/hooks/subagent-cap.sh" }
]
}
]
}
}
3. What happens at the cap
I tested it with SUBAGENT_CAP=2 and a prompt asking for three subagents in a row. Two went through and the third was refused:
PreToolUse:Agent hook error: [.../subagent-cap.sh]: Subagent limit reached: 2 per session. Do not launch more, finish with what you have.
exit 2 blocks the call and hands the message to Claude, which wraps up with what it has instead of retrying. The counter also catches what your subagents launch: the hook fires inside them with the same session_id as the main conversation. To start from zero, delete the counter file.
The number is yours: change the 30 in the script, or export SUBAGENT_CAP before you launch claude. Anthropic's was 200. Check how many you launch in a normal session and set the cap a little above that. Dynamic workflows have their own cap, and I haven't tested this hook with them.
The per-agent limit: maxTurns
The hook counts launches. maxTurns limits how much each agent works, and it's what I ended up putting on delegate:
---
name: delegate
model: sonnet
maxTurns: 777
---
Claude Code enforces it, not the model. In my test, an agent with maxTurns: 2 that I asked to read five files was cut off after the first one and handed back what it had, along with its agentId so you can continue it if needed.
That 777 isn't a typo. A healthy delegate run goes well past 40 turns, so maxTurns works as a circuit breaker: high enough not to cut good work, but finite. Put it on every agent that launches others.
Every brake you have
| Brake | What it limits | Where it goes |
|---|---|---|
PreToolUse hook on Agent |
Total launches in the session | settings.json |
maxTurns |
One agent's turns | The agent's frontmatter |
Leaving Agent out of tools |
That agent launching anything at all | The agent's frontmatter |
CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS |
How many run at once (20; ultracode ignores it) | Environment variable |
CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH |
Nesting levels (3) | Environment variable |
--max-budget-usd |
The run's dollars, subagents included | claude -p only |
What no number fixes
Limits stop the bleeding. These three things in the delegate spec were causing it:
- A verifier that doubts by default. If the spec tells the reviewer to mark
uncertainwhenever evidence isn't clear, anduncertainescalates, escalation becomes the norm on small changes. Save doubt for what genuinely can't be resolved. - Waiting with
sleep. An agent that keeps checking whether its background subagents are done pays for the whole conversation on every check. Claude Code notifies you when a background subagent finishes: end the turn and wait for that notice. - Two rules that collide without saying which wins. A review budget and a quality guarantee can ask for opposite things. If the spec doesn't say which one rules, every run decides differently.
And remember each subagent starts without your conversation: the vaguer the brief, the more it goes around in circles.
Official docs: Concurrent subagent limit · Supported frontmatter fields · Hooks: PreToolUse
Requirements
- Tested on Claude Code v2.1.282 on September 26, 2026, with
python3available for the hook. - No total cap since v2.1.224. The concurrency limit has been there since v2.1.217.