Claude Code Background Agent Labels: The 30-Second Calls and CLAUDE_CODE_FORK_SUBAGENT=0#
Published by Alex Sorokoletov
Background agents in an interactive Claude Code session get a 3-5 word progress label such as "Reading runAgent.ts". A timer refreshes it every 30 seconds, and each refresh where the agent did something new is a model call. CLAUDE_CODE_FORK_SUBAGENT=0 stops those calls for agents started with the Agent tool, at the price of fork agents and forced background launches.
How the label is written#
The label timer starts when an agent launches in the background and stops when the agent finishes. Each time it fires, Claude Code skips the call when the agent's transcript has not changed since the last label or holds fewer than three messages. That skip arrived in v2.1.128, which fixed "sub-agent summaries firing repeatedly while a sub-agent's transcript is static" (changelog). claude -p and other non-interactive sessions never write labels.
When there is something new, the label comes from one of two requests. Remote configuration picks which one; with no configuration, Claude Code uses the full request.
| Mode | What the request sends | Model | Since |
|---|---|---|---|
| Short | Compact notes on the agent's task and recent steps, trimmed to a fixed budget, with prompt caching off | The small fast model (Haiku) | v2.1.302 |
| Full | The agent's whole conversation plus a label instruction, read from the agent's prompt cache without writing to it | The agent's own model | v2.1.128 or earlier |
The short request needs three things: the Anthropic API directly, Haiku available to the account, and neither ANTHROPIC_SMALL_FAST_MODEL nor ANTHROPIC_DEFAULT_HAIKU_MODEL set. On Amazon Bedrock, Google Vertex AI, a gateway that changes the provider, or with either variable set, every label uses the full request.
The full request can also wait. When remote configuration allows it, and the agent's own request has no response yet while its prompt cache is probably cold (by default, no response for 270 seconds), Claude Code holds the label and checks again every 2 seconds, for up to 5 minutes. v2.1.128 also fixed "sub-agent progress summaries missing the prompt cache", which the changelog puts at about a 3x reduction in cache_creation.
Why one agent costs more than another#
A regular subagent starts with only its task, so its full-mode label request carries a short transcript. A fork agent (subagent_type: "fork") starts with a copy of the parent conversation. Its full-mode label request carries that whole conversation plus everything the fork has done since, every 30 seconds while the fork keeps working. A long session with several forks running in the background therefore sends the long context once per fork per refresh, on the session's model.
Fork agents are on by default in the terminal, and v2.1.302 made them the default in claude -p, the Agent SDK, the desktop app and VS Code as well (changelog).
What CLAUDE_CODE_FORK_SUBAGENT=0 changes#
One switch controls three behaviors. With the default, an interactive session gets labels for Agent-tool launches because fork agents are on and subagents are forced into the background. With CLAUDE_CODE_FORK_SUBAGENT=0, all three turn off together.
| Variable | Effect |
|---|---|
CLAUDE_CODE_FORK_SUBAGENT | Fork subagents (subagent_type "fork") start with the full parent conversation and are on by default: in the terminal, and from v2.1.302 in claude -p, the Agent SDK, the desktop app and VS Code. Set it to 0 to turn them off; that also ends forced background launches and 30-second progress labels for Agent-tool launches. |
After setting it to 0:
- Fork agents are gone. Only the regular agent types remain, and each starts with its task alone.
- Subagents are no longer forced into the background. The Agent tool offers
run_in_backgroundagain, and an agent launched without it blocks the main conversation until it returns. - Agent-tool launches get no label. No timer starts, so no label calls are made.
Some agents keep their labels:
- Skills and slash commands that run as a forked agent (
context: fork) still get a label when they run in the background. - A session in coordinator mode still labels its agents.
- An Agent SDK host that turns progress summaries on still gets them.
Add the variable to the env object in ~/.claude/settings.json to apply it to every session, or export it before starting Claude Code. That is why a test with background forks sees the label calls drop to zero: the agents still run, and no label timer starts for them.
Cheaper labels without turning forks off#
There is no setting that turns labels off on their own. Two things lower their cost:
- Keep the short request available. On the Anthropic API, leave
ANTHROPIC_SMALL_FAST_MODELandANTHROPIC_DEFAULT_HAIKU_MODELunset. Either one makes every label a full request on the agent's model. - Launch fewer long-lived background forks. A fork that runs for ten minutes in an active session can send its whole context up to 20 times. Short forks, or regular subagents for work that does not need the parent conversation, keep that number down.
Check which request your labels use#
Run claude --debug and start a background agent. The debug log shows one of these lines on each refresh:
[AgentSummary] Asking for summary from N characters of notes: the short request.[AgentSummary] Forking for summary, N messages in context: the full request. N is the agent's message count, so a fork shows the parent conversation's length.[AgentSummary] Skipping summary for …: transcript unchanged: no call this time.
Related#
- Claude Code prompt cache shows 5m instead of 1h: the other background request that runs every 30 seconds.
- Claude Code environment variables: every variable, with the version it arrived in.