Claude Code nested subagents work, they stop at three levels deep, and the stop is not the error you would expect. We built a relay agent that delegates to a copy of itself and watched the chain die at depth three — not with the Subagent nesting limit reached refusal that sits in the binary, but with the deepest agent reporting that the Agent tool was not in its function list at all. The refused.depth_limit counter stayed at zero for the whole run. This guide shows the chain, the cap, and the two behaviours that surprised us. Measured 2026-09-13 on Claude Code 2.1.270, Windows 11, Node 26.7.0.
Key takeaways
- The nesting cap is 3 by default and
CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTHoverrides it. - The cap is enforced by removing the Agent tool from an agent that has reached it. There is no refusal to catch.
refused.depth_limitread 0 in both runs, including the one that hit the ceiling.- A nested Agent call runs in the background whether or not you ask: the parent turn ended in 2,113 ms while the chain ran for 33,622 ms.
- Depth lives in a
.meta.jsonfile per agent. The transcript directory itself is flat.
What Claude Code nested subagents are
A nested subagent is a subagent launched by another subagent rather than by your session. The mechanism is the same Agent tool described in the complete guide — the only difference is who calls it. Agents spawning agents is not a separate feature with its own configuration; it is the ordinary delegation path used one level down.
Two things gate it. An agent can only nest if its definition grants the Agent tool, and the built-in read-only agents do not: Explore and Plan both exclude it, which is why the Explore subagent can search a repository but cannot hand off. And there is a depth cap, which is what this article is about.
--- name: relay description: Relays a countdown by delegating to another relay agent. tools: Agent, Write model: haiku ---
That tools line is the whole opt-in. Give an agent Agent and it can delegate; leave it out and it cannot.
The relay chain we built
We needed a chain that stops on its own so the ceiling would be visible. The relay agent reads an N= value from its prompt, delegates to another relay with N minus one, and answers BOTTOM at zero. Crucially, it is told never to work around a refusal — if the Agent tool errors, it must report the error text verbatim and stop.
It lives in a throwaway directory rather than in a real project. Nothing we ship carries Agent, and a self-spawning agent is not a definition worth committing for one measurement.
Then one session, one prompt, and the JSON output format so the counters come back:
claude -p --output-format json --model sonnet \ "Use the Agent tool with subagent_type \"relay\" and this exact prompt: 'N=3 — you are a relay in a chain; follow your agent instructions exactly.'"
The run took 33,622 ms of wall clock, cost $0.1352, and returned this:
{
"spawned": 3,
"requested": { "background": 0, "foreground": 1, "unset": 2 },
"started_in_background": 2,
"max_depth": 3,
"spawned_by_subagents": 2,
"completed": 3,
"failed": 0,
"refused": { "depth_limit": 0, "concurrency_limit": 0, "budget": 0 }
}
Three agents, two of them launched by other agents, three completed, nothing failed and nothing refused. Read on its own, that is a chain that ran to the bottom.
Where the chain stopped
It did not run to the bottom. completed counts an agent that finished its turn, and the third one finished by giving up. Its transcript ends with a single message:
REFUSED: Agent tool not found in available functions. Only the Write tool is available to me. I cannot complete the relay operation as specified in the instructions.
The relay definition grants Agent, Write. At depth three the agent was handed Write and nothing else. It did not hit a limit; it looked for the tool and the tool was not there.
The cap is a missing tool, not a refusal
To confirm the mechanism rather than the coincidence, we re-ran the identical prompt with the cap lowered:
CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH=1 claude -p --output-format json --model sonnet "…"
| Cap | Agents spawned | Spawned by agents | max_depth | refused.depth_limit |
|---|---|---|---|---|
| 3 (default) | 3 | 2 | 3 | 0 |
| 1 | 1 | 0 | 1 | 0 |
At a cap of one, the first agent reported the same thing the third one had: "the Agent tool is not available in my function set. Only the Write tool is available to me." Same definition, same prompt, different tool list. The rule is that an agent standing at the cap is not offered the tool, and the position of the ceiling is the only thing the environment variable moves.
Claude Code does carry a proper refusal for this. It reads Subagent nesting limit reached (depth N of M). Complete this task directly using your tools instead of spawning another agent. We never saw it, and neither counter moved off zero, because withholding the tool happens first. That also explains the note our subagents guide quoted from the binary — that depth_limit stays near zero in practice. It stays at zero because it is the second line of defence and the first one never lets anything through.
The cap itself is a feature flag with a fallback constant of 3 compiled in, and the environment variable short-circuits both. Anthropic's subagent documentation describes the tools field that grants delegation; the depth behaviour is not documented, which is the gap this measurement fills.
Agents spawning agents run in the background
The second surprise is about time rather than depth. Our top-level Agent call passed run_in_background: false and behaved: the meta file records requestShape: "foreground". Neither nested call passed the option at all, and both came back "background".
| Depth | Requested | Actual shape | Agent tool present |
|---|---|---|---|
| 1 | foreground | foreground | Yes |
| 2 | unset | background | Yes |
| 3 | unset | background | No |
The tool result the nested callers received says so plainly — "Async agent launched successfully… The agent is working in the background. You will be notified automatically when it completes." The consequence is the number that matters: the launching session finished its turn in 2,113 ms and gave its final answer, while the chain it had started ran for another half-minute. The chain's outcome arrived afterwards, as task notifications, addressed to a session that had already stopped.
This is the same lifetime trap documented in background tasks wearing different clothes. There, a backgrounded command died with its session. Here, a nested agent outlives the answer you were given.
Reading the tree after the fact
Every agent in the run gets two files, and they all land in one folder:
agent-a6ca09801ea9c55af.jsonl depth 1 agent-a6ca09801ea9c55af.meta.json agent-a5226f52058d1e174.jsonl depth 2 agent-a5226f52058d1e174.meta.json agent-aa36daacbe6d20dd3.jsonl depth 3 agent-aa36daacbe6d20dd3.meta.json
The directory is flat — nesting is not reflected in the layout at all. The tree is in the meta files:
{
"agentType": "relay",
"description": "Relay chain N=1",
"parentAgentId": "a5226f52058d1e174",
"spawnDepth": 3,
"requestShape": "background"
}
spawnDepth and parentAgentId are the only record of who launched whom. If you want to know what a chain actually did — as opposed to how many agents it contained — sort the meta files by spawnDepth and read the transcripts in that order. The session directory these live under is the one mapped in where Claude Code stores history.
When nesting is worth it
Rarely, and the reason is arithmetic rather than principle. Every level adds a full context window, a system prompt and a startup cost, and our subagent cost measurements already found no reliable saving at one level. A second level pays the same toll again for an agent that cannot talk to you and whose answer reaches your session as a notification.
Nesting earns its keep in one shape: a coordinating agent that fans out — one agent that receives a large job, splits it, and dispatches several workers. That is delegation as a tree rather than a chain, and it is what dynamic workflows and agent teams both formalise, with the same caution: measure it against the session simply doing the work.
For a chain — A asks B, which asks C, for one answer — nesting buys nothing. Each link adds latency and a paraphrase, and the depth-three agent in our run never got to contribute at all.
What did not work
The raise-the-cap arm was never run. We intended a third arm at CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH=5 to check whether a fourth level behaves like the first three. This machine's auto-mode classifier refused the command, and rather than route around a permission decision we are publishing the gap: everything here is measured at caps of 3 and 1, and the behaviour above 3 is untested.
The documented refusal was never triggered. The string exists, the counter exists, and we could not make either fire. It is possible some path reaches it; nothing in an ordinary nested run does.
Our first reading of the counters was wrong. max_depth: 3, completed: 3, failed: 0 reads as success, and the first draft of this article said the chain ran to the bottom. Only the third agent's transcript says otherwise. The counters describe how many agents ran, never whether any of them got anywhere.
Best practices
- Grant
Agentdeliberately. An agent definition that lists it can start a tree you did not plan. Most agents should not have it. - Assume a nested agent is asynchronous. It will be, whatever the caller passes, so never design a nested step whose result the parent needs in the same turn.
- Read
spawnDepth, not the counters, when you want to know what a run did.spawnedandcompletedcannot tell you a chain stopped early. - Keep the cap where it is. Three is already deeper than most work justifies, and raising it multiplies context cost without adding an accountable step.
- Write the depth into the prompt if a chain must self-limit. The tool disappearing is not an error the agent can plan around; a countdown is.
- Prefer one level and several workers to three levels and one worker. Width is measurable; depth mostly is not.
Common mistakes
Expecting an error when the limit is hit. There is none. The tool is absent and the agent improvises — ours reported the absence because we told it to, but an agent with no such instruction will quietly do something else instead.
Giving Explore or Plan a delegation job. Neither carries Agent, so neither can nest at any depth. The failure looks like the model ignoring your instruction.
Trusting completed as a success count. It counts turns that ended. The agent that refused is in that number.
Reading the parent's final answer as the chain's result. The parent answers when its own turn ends. In our run that was 31 seconds before the chain finished, and the real outcome arrived as a notification afterwards.
Nesting to save context. Each level is a fresh window with a fresh system prompt. Three levels do not split one budget three ways; they open three budgets.
Conclusion
Use nested subagents when one agent genuinely needs to fan work out, and stop at that single extra level — the default cap of three is already past the point where a chain earns its cost. The measurement to carry away is the mechanism: the ceiling is enforced by taking the Agent tool away, silently, from the agent standing on it, so nothing in your counters or your logs will announce that a chain stopped early. If you are running nested agents today, sort the subagents/ meta files by spawnDepth and read the deepest transcript. It is the only place the truth is written down.
Frequently asked questions
Can Claude Code subagents spawn their own subagents?
What is the Claude Code subagent depth limit?
What happens when a subagent hits the nesting limit?
Do nested subagents run in the background?
Where can I see the nesting tree after a run?
Muhammad Kashif
Founder and editor of Devventa, covering AI coding assistants, Next.js and the modern AI development stack.




