Agent(run_in_background: false) does not block — returns spawn metadata instead of the agent's result (v2.1.227)
Environment
- Claude Code 2.1.227, Linux (Ubuntu VM), terminal CLI session, model
claude-fable-5 - Same session as the companion ListAgents report: https://github.com/anthropics/claude-code/issues/85764.
What happened
The model called the Agent tool with run_in_background: false because its next action depended on the agent's result. The tool returned immediately with spawn metadata ("Spawned successfully… agent_id … The agent is now running and will receive instructions via mailbox.") — identical in shape to a background spawn. The agent's actual report only arrived minutes later as an idle teammate-notification.
Expected (per docs)
The Agent SDK subagents documentation describes subagents as running in the background by default, with a foreground run used when the result is needed before continuing — i.e. the call blocks and its tool result is the agent's final message, not spawn metadata.
Actual
The foreground flag is ignored (or silently coerced to background); the caller gets mailbox metadata either way.
Impact
The model cannot serialize on a dependent agent result. Combined with anthropics/claude-code#85764 (running subagents also invisible in the listing), a healthy foreground spawn is indistinguishable from a failed or lost one, which drives wasteful defensive respawns.
Repro sketch
- Have the model call
Agent(name: "fg-probe", run_in_background: false, prompt: <any 2-minute task>). - Observe the tool result arrives in under a second and contains spawn metadata, not the task output.
3 Comments
This breaks a pretty common orchestration pattern: you spawn a worker with run_in_background: false specifically because you need its output to decide the next step -- decompose task, get result, branch on it. When the foreground flag is silently coerced to background, the orchestrator moves forward with spawn metadata instead of the real result, and the downstream branch is wrong without any error to signal why.
Workaround I've been using: poll ListAgents on a short interval after spawn, check for status == "completed" or equivalent, then fetch the result from the mailbox. It works but it's brittle -- you're racing against the agent completing before your first poll and you have to implement your own timeout logic that the SDK should be handling.
A cleaner fix would be to at least document the coercion explicitly and provide a blocking wait primitive (something like WaitForAgent(agent_id, timeout_ms)) so callers don't have to roll their own. Even a synchronous variant of the tool would be better than the current silent mismatch between docs and behavior.
Thanks for the detailed report and repro. I ran both variants on 2.1.233 (Linux, interactive session, agent teams enabled) and confirmed what you saw — but this is intended behavior on current releases, not a bug.
name(your case — the "mailbox" wording confirms it): while agent teams are enabled, a named Agent call launches a teammate, which always returns spawn metadata immediately and communicates via messages; it never blocks. This is documented in How Claude starts agent teams. If you want blocking subagents, turn agent teams off.name: since 2.1.232 (changelog), interactive sessions run all non-teammate subagents in the background, therun_in_backgroundparameter is removed from the tool, and results arrive as completion notifications — see Run subagents in foreground or background. I verified on 2.1.233 that neither variant blocks, and the agent's result arrives moments later. Foreground still applies withCLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1, or in-p/SDK sessions where fork mode is off.On 2.1.227 the non-blocking behavior you hit came specifically from the
nameparameter routing to a teammate. The ListAgents question is tracked separately in #85764.Closing as working-as-documented — reply here if you see a foreground call fail to block with agent teams off and
CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1, and we'll reopen.🤖 Generated with Claude Code
Thanks — that settles it. The
name→ teammate route explains the 2.1.227 behaviour, and I had missed that fork mode became the interactive default in 2.1.232; on 2.1.233 the parameter isn't in the schema at all for an interactive session, so there was nothing left for the call to honour.For the record, the page I quoted (
agent-sdk/subagents) still carries the v2.1.198 note and never mentions fork mode — filed that as #87300 rather than reopening here. Tracking theListAgentshalf in #85764, where I've posted a 2.1.233 measurement and a correction to my own report: the tools reference documents the teammate exclusion, so that behaviour matches the docs too.