Claude Code and Agent SDK Subagents. Fallback Model Chains and Workflows, Done Right
The real schema, depth limits, and trigger conditions for hierarchical spawning and availability-based model fallback - no guessing.
Claude Code ships two primitives that, once you understand them, change how you build agentic systems: hierarchical subagent spawning and availability-based model fallback chains. Both are real and production-ready. Both are also easy to misconfigure, because the surface area is small and the behavior is mostly implied rather than spelled out on a settings page.
This is the field guide I wanted when I first wired them up: the exact AgentDefinition field list, the exact fallbackModel schema, the precise trigger conditions for each mechanism, and a clear accounting of the second, separate “automatic model fallback” that shares the name but not the code path. The goal is simple - configure either feature without guessing.
A note on versions before we start. Claude Code and the Agent SDKs use semantic versioning, not calendar-based labels. As of early July 2026, the current releases are the Claude Code CLI v2.1.204 (npm), the TypeScript Agent SDK @anthropic-ai/claude-agent-sdk v0.3.204 (npm), and the Python Agent SDK claude-agent-sdk v0.2.113 (PyPI). The behavior changes below are pinned to specific releases so you can tell at a glance whether your version has them.
First up: hierarchical subagent spawning, including the complete field list and the behavior changes that landed across v2.1.198 through v2.1.200. Then: the fallbackModel schema, the --fallback-model CLI flag, and the related-but-distinct automatic model fallback mechanism.
Before we get into it, please consider subscribing to my Substack newsletter.
If this kind of practical, production-minded AI work is useful to you, a subscribe, like, comment, or share really helps grow the channel and supports the work.
Thank you for reading and helping these ideas reach more builders.
How Agents Actually Nest: Hierarchical Subagent Spawning
Hierarchical subagent spawning shipped in Claude Code v2.1.172, with the changelog entry stated plainly: “Sub-agents can now spawn their own sub-agents (up to 5 levels deep)” (release notes). An agent can spawn subagents, which can spawn their own subagents, forming a chain of delegation down to five levels below the main agent. The official documentation makes this explicit: a subagent at depth five doesn’t receive the Agent tool and can’t spawn further, regardless of whether it runs in the foreground or background (subagents docs). The limit is fixed and not configurable.
This is the feature that makes Claude Code worth reaching for on complex, multi-stage workflows. One orchestrator delegates to specialists, each of which can delegate to workers, five levels deep. The depth isn’t a theoretical ceiling you’ll never hit - once you start decomposing real tasks, you’d be surprised how fast a planner/generator/evaluator topology eats through levels.
The AgentDefinition Schema, Complete
A subagent is defined programmatically via an AgentDefinition object in the Agent SDK (SDK docs). The complete field set, drawn directly from the schema, is this:
description (string, required) - When Claude should invoke this subagent. The orchestrating agent reads this to decide which subagent to call.
prompt (string, required) - The subagent’s system prompt and role definition.
tools (string[], optional) - Allowed tools. Omit to inherit all tools from the parent.
disallowedTools (string[], optional) - Tools to remove. Supports mcp__server, mcp__server__*, and mcp__* patterns.
model (string, optional) - An alias ('fable', 'opus', 'sonnet', 'haiku', 'inherit') or a full model ID. Defaults to the main model when omitted.
skills (string[], optional) - Skills preloaded into the agent’s context at startup.
memory ('user' | 'project' | 'local', optional) - Memory source for this agent.
mcpServers ((string | object)[], optional) - MCP servers available to this agent, by name or inline config.
initialPrompt (string, optional) - Auto-submitted as the first user turn when this agent runs as the main-thread agent; ignored when invoked as a subagent.
maxTurns (number, optional) - Cap on agentic turns before the agent stops.
background (boolean, optional) - Force non-blocking background execution.
effort ('low' | 'medium' | 'high' | 'xhigh' | 'max' | number, optional) - Reasoning effort override.
permissionMode (PermissionMode, optional) - Permission mode for tool execution within this agent.
That is 13 fields. If you’re configuring subagents as markdown files in .claude/agents/ rather than through the SDK, the frontmatter schema is a slightly different (richer) shape - it adds name, hooks, isolation, color, and a few others, and treats the file body as the prompt. The 13 fields above are the SDK’s AgentDefinition; don’t conflate the two.
Context Isolation: What Each Agent Can See
Here is what catches engineers off guard the first time they build a multi-level hierarchy: a subagent starts with a fresh, isolated context. It receives its own prompt, the delegation message the parent composes when handing off the work, the project CLAUDE.md and memory hierarchy, and the tool definitions inherited or specified for it (subagents docs).
It does not receive:
The parent agent’s conversation history
The parent’s tool call results
The parent’s system prompt
Think of it like a contractor who shows up knowing only the job brief you handed them, not everything said in last week’s staff meetings. If the subagent needs context from the parent, you pass it explicitly through the delegation message or bake it into the subagent’s prompt. Nothing flows down automatically. This is a feature, not a limitation: it keeps agents composable and prevents context bloat from cascading through every level of a five-deep hierarchy. The one exception is a fork, which inherits the parent’s conversation instead of starting fresh.
Blocking Further Spawning
Want to prevent a subagent from spawning further subagents? Two approaches work, and both produce the same result - the subagent can’t invoke the Agent tool (subagents docs).
Option A - omit Agent from the tools array:
{
“description”: “Leaf worker: handles file analysis only, cannot delegate”,
“prompt”: “You analyze files and return structured summaries. Do not attempt to spawn other agents.”,
“tools”: [“Read”, “Glob”, “Grep”],
“model”: “haiku”
}Option B - add Agent to disallowedTools:
{
“description”: “Leaf worker: handles file analysis only, cannot delegate”,
“prompt”: “You analyze files and return structured summaries.”,
“disallowedTools”: [“Agent”],
“model”: “haiku”
}disallowedTools is preferable when the subagent otherwise inherits a broad tool set, and you want to remove only the delegation capability without enumerating every other allowed tool.
Assigning Models With the model Field
The model field accepts either a tier alias or a full model ID string:
Aliases:
'fable','opus','sonnet','haiku','inherit'Full model IDs: e.g.
'claude-opus-4-8','claude-haiku-4-5','claude-fable-5'
The 'inherit' alias tells the subagent to use the same model as the parent. Omitting the field entirely defaults to the main model, which is functionally equivalent to 'inherit' in most configurations. The common cost-and-latency move is to run your orchestrator on Sonnet or Opus while the leaf workers that just read files and extract text run on Haiku.
Behavior Changes That Matter: v2.1.198 through v2.1.200
Three consecutive releases tightened and improved subagent behavior in ways that matter for production agents. Each change is additive - none removes a capability - but together they shift defaults and improve resilience under API errors (release notes).
v2.1.198: Background by Default, and Extended-Thinking Inheritance
Before v2.1.198, whether an agent ran in the foreground or background was something you configured explicitly. Starting with this release, background execution is the default. When Claude Code needs a synchronous result from a subagent - because the next step depends on it - it explicitly runs that subagent in the foreground. In practice: if you don’t set the background field in your AgentDefinition, the subagent runs in the background, and your orchestrator handles the asynchronous result.
The second change in this release: subagents now inherit the main session’s extended-thinking configuration. Previously, extended thinking was disabled inside subagents regardless of what the parent session had configured. If your main session runs with effort: 'high' or a specific thinking budget, subagents now respect that setting by default unless their own effort field overrides it. The practical effect is noticeably better output quality on delegated tasks and during context compaction.
If you are a paid subscriber, thank you. Your support makes this work possible.
If you are a free subscriber and find these articles useful, please consider upgrading. A paid subscription is $80 per year or $8 per month.
v2.1.199: Partial Output Is Preserved on API Error
Before v2.1.199, if an API error ended a subagent mid-run, any text the agent had already produced was discarded. The parent received nothing and had to decide whether to retry without knowing how far along the subagent was.
Starting with v2.1.199, a subagent that has already produced text returns its partial output, annotated to indicate that the run ended early due to an API error. This is a resilience improvement, not a new failure mode: the subagent still didn’t complete its task, but its partial work is recoverable rather than lost. The same release also fixed streaming responses being discarded when the API emitted a mid-stream overloaded/server error after partial output - the partial is now kept with an incomplete-response notice.
v2.1.200: Empty Output Becomes an Explicit Failure
Before v2.1.200, a subagent cut off by a rate limit silently returned an empty result. The parent had no reliable way to distinguish “subagent returned an intentionally empty result” from “subagent failed before producing anything.”
Starting with v2.1.200, that silent empty return becomes an explicit failure instead of failing silently. Orchestrators can now distinguish a genuine empty result from a cut-off run and route accordingly - retry, escalate, or log - rather than treating empty output as ambiguous. Together, v2.1.199 and v2.1.200 close a class of silent failures: partially-run subagents surface their partial output, and subagents that produced nothing surface a deterministic error. Both changes favor observability over silent continuation.
When to Reach for Workflow Instead
Hierarchical subagent spawning is built for turn-by-turn delegation with modest fan-out: a few parallel agents, a few levels of nesting, tasks where each result feeds the next decision.
For orchestrating dozens to hundreds of agents whose coordination logic is complex, or whose execution graph doesn’t map cleanly to a turn sequence, Claude Code provides a dedicated Workflow primitive. A Workflow moves orchestration into a script rather than into an agent’s reasoning loop: the script defines the execution graph explicitly, supports fan-out at scale, and doesn’t consume turns for routing decisions.
The practical decision boundary: if you find yourself writing a subagent whose primary job is to call other subagents and route their outputs rather than to do real work itself, that routing logic likely belongs in a Workflow script.
fallbackModel: Real Schema, Real Triggers
Here is the entire fallbackModel schema:
{
“fallbackModel”: [“claude-sonnet-4-6”, “claude-haiku-4-5”]
}That’s it. A plain ordered array of model name or alias strings. No per-entry object fields. No maxTokens. No costCeiling. No priority weights. Entries are strings, and the array is ordered by preference. The setting landed in v2.1.166, which the changelog describes as configuring “up to three fallback models tried in order when the primary model is overloaded or unavailable” (release notes).
If you’ve seen fallbackModel documented as an array of objects like { "model": "claude-sonnet-4-6", "maxTokens": 8192, "costCeiling": 0.05 }, that format doesn’t exist in the real configuration. The schema is simpler than that - plain strings, in priority order.
The --fallback-model CLI Flag
The equivalent CLI flag accepts a comma-separated list of model names or aliases:
claude --fallback-model sonnet,haiku
As of v2.1.166, this flag also applies to interactive sessions, not just print-mode runs. It’s session-scoped: it applies only for the duration of that session and takes precedence over the settings.json value for that session. When the session ends, settings.json is used again the next time Claude Code starts. That makes the flag handy for testing a fallback chain at the command line without committing the change to settings.
The Three-Model Cap
The fallback list is capped at three models, tried in order. Extra entries beyond three are ignored. If you list the same model under two names that resolve to the same underlying model, deduplicate before you count - you get three distinct fallbacks, not three slots to waste on duplicates.
What Activates Fallback (and What Doesn’t)
This is the part most worth memorizing. Fallback activates only when the primary model hits a specific subset of server-side failure conditions (release notes).
Fallback triggers on:
Primary model overloaded (capacity unavailable)
Primary model unavailable (service down or unreachable)
Primary model returns a non-retryable server error - Claude Code retries the turn once on the fallback model when the API rejects an unexpected non-retryable error
Fallback does not trigger on:
Rate-limit errors - these hit the normal rate-limit handling path (backoff, retry), not the fallback chain. As of v2.1.199, transient server rate-limit errors (429s unrelated to your usage limit) are retried automatically with backoff rather than failing the turn, but that’s the retry path, not model fallback.
Authentication errors - wrong key or missing credentials; a different model with the same key faces the same problem.
Billing or account errors - account or plan issues; a different model on the same account faces the same state.
Request-size errors - the request is malformed or too large; routing it to a different model sends the same broken request.
Transport errors - network-level failures below the API layer.
The rate-limit distinction matters in practice. If your system is being rate-limited, fallbackModel will not help. Rate-limit handling - whether through backoff, queue management, or spreading requests across API keys - is a separate concern that fallbackModel doesn’t address.
Turn Scope: The Primary Model Retries Every Message
For the overload and unavailability cases, the fallback switch is turn-scoped. If the primary model is overloaded on turn N and Claude Code routes to the first fallback, that fallback handles only turn N. On turn N+1, Claude Code attempts the primary model again first.
fallbackModel is an availability hedge, not a permanent model downgrade. The system tries to return to the primary model at every opportunity. If the primary is still unavailable on turn N+1, it falls back again in the same priority order.
One nuance added in v2.1.200: when the primary model is not found (as opposed to overloaded), Claude Code now switches to your configured --fallback-model for the rest of the session rather than failing every request. So treat the trigger as the deciding factor - overload and unavailability are per-turn; a missing model is session-sticky.
Valid Entry Formats
Each element in the fallbackModel array accepts:
A model alias:
"sonnet","haiku","opus","fable"A full model ID: e.g.
"claude-sonnet-4-6","claude-haiku-4-5","claude-opus-4-8"The literal string
"default", which expands to the account or organization default model
The "default" alias is useful when you want the last-resort fallback to be whatever your account is configured to use, without hardcoding a specific model ID that might go stale as new versions release.
Automatic Model Fallback: A Distinct, Safety-Classifier-Driven Mechanism
Claude Code includes a second fallback mechanism called automatic model fallback. It shares the name and the observable outcome - a request gets routed to a different model - but it’s triggered by entirely different conditions, applies only to a specific model tier, and isn’t configurable via fallbackModel at all.
Automatic model fallback is content-based, not availability-based. It’s specific to Claude Fable 5 and its safety classifiers. When a request to Fable 5 trips a safety classifier - for example, a request touching offensive cybersecurity techniques, biology or life-science experimental methods, molecular mechanisms, or attempts to extract a model’s thinking process - the request is automatically routed to an Opus model instead. This routing isn’t silent: Claude Code shows a notice in the transcript when the switch occurs. You don’t configure it, and you can’t disable it by editing fallbackModel.
The Opus target is fixed. Unlike availability-based fallbackModel, automatic model fallback has no user-configurable list of candidates. You get Opus - specifically the best available Opus model on your provider (Opus 4.8 via the Anthropic API; the best available Opus elsewhere) - and you don’t get to substitute something else. Anthropic has noted this fallback fires in fewer than 5% of sessions.
Why Conflating the Two Breaks Your Configuration
Engineers configuring fallbackModel for availability resilience sometimes expect it to interact with classifier-based routing. It doesn’t. The two mechanisms operate on completely separate code paths.
If your primary model is Claude Fable 5 and it’s overloaded, the availability-based
fallbackModelchain activates and routes to your configured fallback list.If your primary model is Claude Fable 5 and a safety classifier fires, automatic model fallback activates and routes to Opus, regardless of what
fallbackModelcontains.
Configuring a non-Opus model in fallbackModel doesn’t affect automatic model fallback. Automatic model fallback doesn’t consume an entry from your fallbackModel list. The two mechanisms don’t interfere with each other; they just happen to share the word “fallback.”
Availability-Based vs. Safety-Classifier Fallback, Side by Side
Two mechanisms, two completely different triggers. Here’s how they contrast:
Availability-based fallbackModel:
Trigger: overload, unavailable, or non-retryable server error
Configured via:
settings.jsonfallbackModelarray or the--fallback-modelCLI flagEntries: any model name or alias string, up to three, tried in order
Scope: turn-scoped for overload/unavailability (primary retried next message); session-scoped for model-not-found as of v2.1.200
Applies to: any primary model
Automatic model fallback:
Trigger: safety-classifier flag (cybersecurity, biology/life-science, distillation, and other flagged domains)
Configurable: no; built into Fable 5 routing
Target: Opus only (Opus 4.8 on the Anthropic API; the best available Opus on other providers)
Notice: shown in the transcript when the switch occurs
Applies to: Claude Fable 5 only
When to Use Which
fallbackModel is an engineer-configured availability hedge: when your primary model is down or overloaded, try these alternatives in order, one turn at a time. Configure it in settings.json or pass it as a CLI flag. It does exactly what you tell it.
Automatic model fallback is a platform-level safety routing decision: when a Fable 5 request trips a classifier, use Opus. No configuration required or accepted.
Use fallbackModel to keep your application running during partial outages. Understand automatic model fallback so you can explain to users why a Fable 5 request occasionally produces an Opus response, and so you don’t spend time trying to configure away a mechanism that isn’t configurable. Those are two very different problems, and treating them as one will send you chasing solutions that don’t exist.
The Short Version
Subagents can nest five levels deep (since v2.1.172). The cap is fixed, not configurable, and applies to foreground and background alike.
An
AgentDefinitionhas 13 fields;descriptionandpromptare required. Subagents start with a fresh, isolated context - pass parent context explicitly.Block further spawning by omitting
Agentfromtoolsor adding it todisallowedTools.fallbackModelis a plain array of strings, up to three, tried in order. No object fields, nomaxTokens, nocostCeiling.Fallback triggers on overload, unavailability, and non-retryable server errors - not on rate limits, auth, billing, request-size, or transport errors.
Overload/unavailability fallback is turn-scoped; model-not-found is session-scoped (v2.1.200).
The Fable 5 safety-classifier fallback to Opus is a separate, non-configurable mechanism. It doesn’t touch your
fallbackModellist.
Configure either feature with that picture in mind and you won’t have to guess.
Related Articles
If this sparked ideas you want to follow further, these pieces cover the patterns directly:
Claude Code Subagents and Main Agent Coordination: A Complete Guide to AI Agent Delegation Patterns - The implementation companion. Walks through the coordination patterns the
AgentDefinitionschema enables: how an orchestrator decomposes tasks, how context flows explicitly across delegation boundaries, and how to design reliable multi-level agent systems.Why Single-Agent AI is Dead: Inside Anthropic’s New Blueprint for Long-Running Agents - The architectural case for why hierarchical subagent spawning matters. Covers Anthropic’s GAN-style Planner/Generator/Evaluator architecture and why single-agent systems structurally fail on long-horizon tasks.
The State Problem: Why Your Agent Forgets and What to Do About It - Digs into the context isolation problem: subagents start fresh and receive none of the parent’s conversation history, tool results, or system prompt. Covers why this is architectural rather than a bug, and the patterns for explicitly passing context across agent boundaries.
What Claude Managed Agents Actually Ships - Covers the production primitives in Claude Managed Agents - native cron scheduling, the Outcomes maker/checker system, and multi-agent coordination - built on top of the same
AgentDefinitionandfallbackModelprimitives documented here.The Maker/Checker Split, Three Ways - Examines the verification architecture that makes multi-level subagent hierarchies reliable in practice: separating agents that generate work from agents that evaluate it. Directly applicable to teams designing hierarchical systems where some levels act as evaluators rather than generators.
If this helped you, please consider subscribing to my Substack newsletter.
Likes, comments, and shares really do make a difference. They help grow the channel, support the work, and get these ideas in front of more builders who are trying to understand AI agents, harness engineering, and production AI systems.
Thank you for reading and for helping the work reach more people.
If you are a paid subscriber, thank you. Your support makes this work possible.
If you are a free subscriber and find these articles useful, please consider upgrading. A paid subscription is $80 per year or $8 per month.
Free subscribers typically receive access to the full versions of paid articles after one to two months.
About the Author — Claude Certified Architect
Rick Hightower is a former Senior Distinguished Engineer at a Fortune 100 company, focusing on delivering ML / AI insights to front-line applications, and a practitioner building multi-agent production systems. Follow him on SubStack and Medium for more hands-on agent engineering content. You can also book him to speak and train your team: Check out Rick Hightower’s SpeakerHub.
Rick Hightower helps companies become AI-first through practical mentoring, executive and team training, and custom AI solution development. He is a former Senior Distinguished Engineer at a Fortune 100 company, where he focused on bringing ML and AI insights into real front-line business applications.
Subscribe to Rick’s newsletter to see videos and guides.
Rick is a Claude Certified Architect, AI systems practitioner, and builder of production multi-agent systems. He is currently working on authoring a book on Harness Engineering with Manning Publishing. He created Skilz, a universal agent skill installer supporting 30+ coding agents, including Claude Code, Gemini, Copilot, and Cursor, and co-founded one of the largest agentic skill marketplaces.
Today, Rick and the Spillwave team work with leaders and teams who want to move beyond AI experiments and build real AI capability inside their companies. He helps organizations adopt AI safely, train their people, redesign workflows, and build practical AI systems that create measurable business value.
Ready to make your company AI-first? Connect with Rick on LinkedIn, Substack, or Medium, book him to speak or train your team, or visit Spillwave to explore mentoring, training, and custom AI solutions for your organization.





