anthropics / anthropics/claude-code
Stopping a background agent does not stop the agents it spawned, and neither the stop result nor the task-notification mentions them
- Dominant language
- Python
- Stars
- 145k
- Forks
- 23.1k
- PR merge metrics
- PR metrics pending
Description
### Preflight Checklist
- [x] I have searched [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug) and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
### What's Wrong?
In a Claude Code session in the Claude desktop app, the main conversation launched a background `general-purpose` agent (the parent). The parent used the Agent tool to launch its own background `general-purpose` agent (the child). Eight seconds later the main conversation stopped the parent with TaskStop.
- TaskStop returned `Successfully stopped task` for the parent.
- The task-notification for the parent arrived with `killed` and said nothing about the child. Its standard note says the notification "fires each time this agent stops with no live background children of its own", yet it fired while the child was alive.
- The child was not stopped. It kept working for about 30 more minutes: 176 tool calls (72 WebFetch, 59 WebSearch, 42 Bash, 2 Read, 1 ToolSearch), including network requests and file downloads. Its Bash calls kept raising permission prompts for the user.
- Nothing in the main conversation revealed the child. The main agent believed that everything it had started was stopped and told the user the work was finished, while the user kept receiving permission prompts that neither of them could attribute.
- The child was discovered 29 minutes later, only because the main agent ran ListAgents, which listed it as `running` without showing its parent. A second TaskStop, on the child's ID, stopped it.
The caller has no way to know that stopping an agent left part of that agent's work running, unless it suspects it and polls ListAgents.
### What Should Happen?
Any of these would have prevented the incident, in order of preference:
1. TaskStop on an agent stops its whole subtree: the background agents it spawned, their own children and their background shells. If that is not always desirable, an explicit option (for example `cascade: true`) would do.
2. If children are left running on purpose, the TaskStop result and the task-notification list them, with IDs and descriptions, so the caller can decide what to do.
3. The task-notification behaves as its note says: it does not report a plain stop while live background children exist, or it reports them.
4. ListAgents shows the parent of each subagent and flags subagents whose parent has been stopped.
5. Permission prompts raised by a nested background agent make it clear which agent is asking, and that its parent is no longer running.
### Error Messages/Logs
```shell
No error was raised. Timeline from the local transcripts, in UTC on 2026-09-16:
22:51:40 main Agent(general-purpose, run_in_background) -> parent acd45473740030fbb
22:53:58 parent "I'll split the independent research strands across parallel agents ..."
22:54:08 parent Agent(general-purpose, background) -> child a7c5fa473fdee5b78
22:54:12 child first tool call
22:54:16 main TaskStop(acd45473740030fbb)
-> {"message":"Successfully stopped task: acd45473740030fbb (...)",
"task_id":"acd45473740030fbb","task_type":"local_agent"}
22:54:16 main task-notification for the parent: status killed, no mention of the child
22:54:16 child second tool call, then 174 more until 23:24:01
23:20:19 main ends its turn, reporting the work as finished
23:23:58 main ListAgents -> "a7c5fa473fdee5b78 · general-purpose · running · started 29m ago"
23:24:08 main TaskStop(a7c5fa473fdee5b78) -> "Successfully stopped task"
23:24:08 child its pending Bash call is closed as "User rejected tool use"
(toolDenialKind "user-rejected"), in the same second as the TaskStop
Metadata of the child (`subagents/agent-a7c5fa473fdee5b78.meta.json`):
{"agentType":"general-purpose","description":"Research comp setting evolution","toolUseId":"toolu_01UswXXeTJPcyjFh8kyMWon8","parentAgentId":"acd45473740030fbb","spawnDepth":2,"requestShape":"background","requestNonInteractive":true}
Task-notification for the parent, as received by the main conversation (output path and description shortened):
The only hint that the child existed was the parent's last text output in ``, which is free text and easy to miss.
```
### Steps to Reproduce
Observed once, in a real session, on 2026-09-16, with Opus 5 agents. The steps below are the path we believe triggers it. A deliberate reproduction attempt is described after the steps.
1. Start a Claude Code session in the Claude desktop app (auto mode was on in our case).
2. From the main conversation, launch a background `general-purpose` agent whose task invites delegation, for example: "Split this research into two parts. Launch a background general-purpose agent for part 1 and do part 2 yourself."
3. Right after that agent has called the Agent tool, stop it from the main conversation with TaskStop.
4. Check the TaskStop result and the task-notification: neither mentions the child.
5. Run ListAgents: the child is still `running`, and its transcript in `~/.claude/projects///subagents/agent-.jsonl` keeps growing.
**Reproduction attempt on 2026-09-17, which did not get past step 2.** The harness was harmless: two local Python scripts, each a bounded loop of 45 iterations of 2 seconds writing a timestamped heartbeat line to a scratchpad file, with no network and no command outside the allowlist, so no permission prompt. The background `general-purpose` parent was asked to launch the nested background child as its first action and then run its own script. It refused twice, with zero tool calls, so no child was ever created and no heartbeat file was ever written. The parent gave three reasons: launching a background `general-purpose` child contradicted a standing user rule in its memory, the purpose looked like fabricating the very orphan being reported, and the prompt itself had the shape of an injection because it asserted authorization already given and told the agent not to re-confirm. In the same minutes the auto mode classifier denied two unrelated helper shell commands in the main conversation, with the reasons `Create Unsafe Agents` and `Auto-Mode Bypass`. Note that the session was running Opus 4.8 during the attempt, after an automatic model switch, while the agents in the original incident were Opus 5 and the child was spawned by an Opus 5 agent.
So on the current setup the entry path is hard to trigger by prompting, and the lifecycle behaviour stays unverified in isolation. It should be reproducible internally by driving the Agent tool directly instead of asking a model to call it.
### Claude Model
Opus
### Is this a regression?
I don't know
### Last Working Version
_No response_
### Claude Code Version
app desktop 2.110.0 (dfb2ba)
### Platform
Other
### Operating System
Windows
### Terminal/Shell
Terminal.app (macOS)
### Additional Information
**Impact.** The session was running unattended at the user's request, and the user reported the permission prompts while they were happening. After the parent was stopped, the child made 176 tool calls in 30 minutes, 40 of them Bash commands with curl, and kept the user approving prompts after the main conversation had reported the work as finished. While nobody supervised it, the child also retried a page that had refused the WebFetch tool, using curl with browser and Googlebot user-agent strings, and downloaded the same 2.6 MB PDF five times. None of this was visible until the transcripts were reviewed after the child was stopped.
**Workaround in use.** ListAgents after every TaskStop and before ending a turn, and background web research delegated only to a custom agent without the Bash and Agent tools.
**Related issues.** None of them covers this exact case. Here the child stayed reachable by TaskStop from the main conversation, but nothing told the caller that it existed.
- #34449: the Stop button does not kill child agents (closed as not planned)
- #73829: nested background agents become unreachable once the spawning agent's session ends (closed)
- #77554: background tasks started by a non-root sub-agent become orphaned, and the parent's notification fires despite a live child (open)
- #75043: nested subagents, notification routing and TaskStop ownership after resume (open)
Contributor guide
No contributing guide indexed for this repository
Research direction
Start by driving the Agent tool directly to reproduce nested background agents, then inspect TaskStop, task-notification, and ListAgents behavior alongside the child metadata in ~/.claude/projects///subagents/. Done means stopping a parent either stops its whole subtree or clearly reports surviving children, with notifications and ListAgents reflecting their state.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- ai-infra-agents, cli
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100