Opus subagents ignore shutdown_request while continuing to emit idle_notification (sonnet agents approve normally)

Status Open
Maintainer reply None cached
Activity 0 comments · opened Jul 27, 2026

Summary

Subagents spawned via the Agent tool ignored shutdown_request (sent via SendMessage) while continuing to emit idle_notification messages. In one session, 6 shutdown requests produced 2 approvals — and the 4 unacknowledged requests were both sent to the two agents running model: opus, while both model: sonnet agents in the same session approved normally on the first request.

Because each unacknowledged agent keeps emitting idle_notification messages, and each notification is delivered to the parent wrapped in a multi-hundred-token preamble and wakes the parent for a turn, a stuck agent imposes an ongoing token cost until it is force-stopped.

Environment

  • Claude Code CLI, macOS (Darwin 25.5.0), zsh
  • Parent session model: Opus
  • task_type reported by TaskStop: in_process_teammate
  • backendType reported in approval messages: tmux

Steps to reproduce

  1. Spawn 4 background subagents in a single message via Agent, with model set explicitly — 2 with model: "opus", 2 with model: "sonnet". (In my case each was given a distinct set of files to edit; the work itself completed successfully in all 4.)
  2. Let all 4 finish their work and go idle. Each emits {"type":"idle_notification","idleReason":"available"}.
  3. Send each one {"type": "shutdown_request", "reason": "..."} via SendMessage.
  4. Observe which agents return shutdown_approved.

Expected

All 4 agents return {"type":"shutdown_approved","requestId":"..."} and terminate, as documented in the SendMessage tool description.

Actual

  • The 2 sonnet agents each returned shutdown_approved within ~1s and terminated cleanly (teammate_terminated received for both).
  • The 2 opus agents returned no shutdown_approved. Instead each emitted another idle_notification at roughly the same timestamp as the shutdown request.
  • I re-sent shutdown_request to both non-responders. Both again produced only idle_notification messages — 3 more across the two agents — and neither acknowledged.
  • TaskStop (passing the bare agent name as task_id) succeeded on the first attempt for both, returning Successfully stopped task: <id> with task_type: in_process_teammate.

So the agents were alive and responsive enough to emit notifications, but never processed the shutdown_request control message. Notably, these same 2 agents had also completed their work without sending a final report message, while the 2 sonnet agents both reported normally — so the failure to handle protocol/outbound messaging appears correlated across both directions, not specific to shutdown.

Timeline (times UTC, one session)

| Time | Event |
|---|---|
| 23:22–23:26 | All 4 agents complete work; 2 report results, 2 report nothing |
| ~23:46:13 | shutdown_request sent to all 4 |
| 23:46:17 | agent C (sonnet) → shutdown_approved, terminated |
| 23:46:17 | agent D (sonnet) → shutdown_approved, terminated |
| 23:46:17 | agent A (opus) → idle_notification (no approval) |
| 23:46:24 | agent B (opus) → idle_notification (no approval) |
| ~23:46:29 | shutdown_request re-sent to agents A and B |
| 23:46:33 | agent A → idle_notification (no approval) |
| 23:46:36 | agent B → idle_notification (no approval) |
| ~23:46:45 | TaskStop on agent A → success, first attempt |
| ~23:46:47 | TaskStop on agent B → success, first attempt |

Impact

  1. Token cost. Every idle_notification is delivered to the parent wrapped in a standing preamble and triggers a parent turn (prefill of full context + output). An agent that ignores shutdown but keeps emitting notifications bills the parent indefinitely. This is small per event but unbounded in duration, and it is pure overhead — the agent's work was already complete.
  2. No signal that the handshake failed. SendMessage returns {"success":true, ...} for a shutdown_request that is never processed. From the parent's side a dropped request is indistinguishable from one in flight, so the only way to notice is to observe that no shutdown_approved ever arrives.
  3. Misleading guidance. The SendMessage docs frame shutdown_request as the way to terminate a teammate and caution against originating one unless asked. There is no documented fallback for a non-responsive agent — TaskStop turns out to be strictly more reliable and needs no cooperation, but nothing points you there.

Suggested fixes

  • Make shutdown_request handling not depend on the agent's model or its inference loop — terminate at the harness level after a grace period.
  • Have SendMessage surface a timeout/nack for an unacknowledged shutdown_request rather than a bare success: true.
  • Suppress idle_notification from an agent with a pending shutdown request; a shutting-down agent has nothing useful to report.
  • Document TaskStop as the fallback for a non-responsive teammate.

Workaround

Use TaskStop with the bare agent name instead of the shutdown_request handshake. It worked first try on both stuck agents and does not require the agent to cooperate.

View original on GitHub ↗