Repository navigation
CLI: follow-up to an idle background agent is delayed by minutes to hours while its status shows "running" #5143
Description
Filed with the help of GitHub Copilot CLI (model: Claude Opus 5.5). Root-cause analysis and any proposed fix were AI-assisted and human-reviewed before filing.
Summary
write_agent behaves very differently depending on the background agent's state:
- Steering a running agent works. The steer took effect 12 s after
write_agentreturned
(23 s after the call; the agent was finishing a 15 s sleep). - A follow-up to an idle agent (its turn has finished) is reported as delivered, but the new
turn starts minutes to hours later: 6.5 and 9.2 min for two trivial repro agents, about 8.5 h
in a long, busy session. Meanwhileread_agentreportsstatus: runningwith no new turn. - There is no cancel/stop operation for background agents; a stalled one stays
running.
Impact
- Coordinator workflows that give an idle agent its next step stall for minutes to hours, with no
error and no timeout. - A queued agent looks exactly like a busy one:
status: running, with no tool-call count or a
stale one from the previous turn. Even a "stop" sent withwrite_agentwaits behind the delay. - Workaround: never send follow-ups to idle agents; start a fresh one-shot agent with full context.
Root cause (observed/suspected)
read_agent shows the idle state and the final answer in real time. Follow-up dispatch instead
appears to follow the session event stream, which trails real time for background agents:
- Observed: an agent's events (messages, tool calls,
subagent.completed) are recorded
minutes to hours late: 5-10 min in the repro, 6-9 h in the long session. The lag differs per
agent: during the repro, the long-session agent in the same session was still hours behind. - Observed: in all three cases, the queued follow-up's
user.messagewas recorded 0.4-3 s
after the previous turn's latesubagent.completed. - Observed:
read_agent'stool_calls_completedequalled the number of the previous turn's
tool calls recorded in the lagging stream at that moment (9 of 13). - Suspected: a queued follow-up is dispatched only once the previous turn's completion drains
through that stream, not when the agent goes idle. Steering bypasses this path.
Log signature / evidence
Local times. Event times are as recorded in events.jsonl; first actions (file writes, Get-Date) are ground truth.
| Agent A (steered, then follow-up) | Agent B (follow-up only) | |
|---|---|---|
| Previous turn | 13 tool calls; last write 04:27:26 | text-only reply, 0 tool calls |
read_agent shows idle |
04:30:31 | 04:23:07 |
Follow-up sent (write_agent: "Message delivered") |
04:30:44 | 04:23:30 |
read_agent while waiting |
status: running, ... total_turns: 1, tool_calls_completed: 9 |
status: running, ... total_turns: 1 (timed out at 04:26 and 04:29) |
Previous turn's subagent.completed recorded |
04:36:52.1 | 04:32:33.7 |
Follow-up user.message / assistant.turn_start |
04:36:52.5 / 04:36:55.8 | 04:32:35.5 / 04:32:36.9 |
| First follow-up action | 04:37:13 (+6.5 min) | 04:32:43 (+9.2 min) |
Also from the repro: the steer took effect at 04:23:05, but its user.message was recorded at
04:28:30; A's first tool call (it wrote at 04:21:33) was recorded at 04:26:30. B's turn reports
durationMs: 624022 in subagent.completed, yet read_agent showed it done within 170 s of launch.
Long, busy session (one general-purpose background agent):
- It finished a 183-tool-call turn (last file change 21:48;
read_agentshowedidleat 22:07).
A follow-up asking for more work was sent at 22:08. read_agentat 22:13 and 22:15:status: running, ... total_turns: 1, tool_calls_completed: 22.- At 22:37 a second
write_agenttold it to stop and reply with one word; it returneddelivered, task_status=running. - At 06:41 the next day the previous turn's
subagent.completedwas recorded. The follow-up
started 3 s later and the stop message 11 s after that, so the agent replied "stopped" and did
no work. The follow-up had waited about 8.5 h.
Steps to reproduce (ISOLATED)
Everything writes only to a throwaway folder, shown as <scratch>.
- In an interactive session, have the main agent start two general-purpose background agents:
- A: "Make 12 separate shell calls. Each runs
Start-Sleep 15, then appends
<HH:mm:ss> step <n>to<scratch>\steer.txt. Then replysteer done." - B: any trivial task that ends with a text-only reply.
- A: "Make 12 separate shell calls. Each runs
- While A is still looping, steer it with
write_agent: "Append<HH:mm:ss> ackto
<scratch>\ack.txt, then continue."ack.txtappears about 12 s afterwrite_agentreturns. - Once
read_agentshows Bidle, send it a follow-up: "Make three separate shell calls running
Get-Date -Format 'HH:mm:ss', then replyturn1 doneplus the times." - Once A is
idle, send it a follow-up: "Make two separate shell calls, each appending
<HH:mm:ss> turn1 call <n>to<scratch>\turn1.txt, then replyturn1 done." - Poll
read_agentand note when each follow-up's first action happens.- Expected: both follow-ups start within seconds, like the steer.
- Actual: both show
runningwith no new turn, then start after 9.2 min (B) and 6.5 min
(A), each right after its previous turn'ssubagent.completedis recorded.
Conditions: the tasks were isolated, but the session was not idle. It was long-running, with
28 idle background agents and five others running: three actively working, and two that had
shown running for hours (one is the long-session agent above). The hooks listed under
Environment were active. Not yet confirmed in a fresh, otherwise idle session.
Proposed fix (optional)
- Deliver a queued follow-up as soon as the agent is idle (the state
read_agentalready
reports), and emitsubagent.completedwhen the turn actually ends. - Report the real state: "queued, not started" (with its age) instead of
running, without
the previous turn's tool-call count. - Add a cancel/stop operation for background agents that does not depend on message delivery.
Related
- Background sub-agents (general-purpose) get stuck indefinitely, never complete #3350: background agents stay
runningwith no completion notice. The late
subagent.completeddescribed here may be the same mechanism. - write_agent may block until the target background agent starts actively processing, causing new user input to queue #4101:
write_agentto an idle agent blocks the caller. Here it returns at
once, but the agent's next turn starts late.
Environment
- Copilot CLI
1.0.96-2 - Windows, x64
- Model: Claude Opus 5.5 for the main session; background agents were
general-purpose, with no
model override preToolUse/postToolUsehooks configured; they also fire for background-agent tool calls
Activity