Skip to content

CLI: follow-up to an idle background agent is delayed by minutes to hours while its status shows "running" #5143

Open

Description

@yumikohey

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_agent returned
    (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. Meanwhile read_agent reports status: running with 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 with write_agent waits 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:

  1. 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.
  2. Observed: in all three cases, the queued follow-up's user.message was recorded 0.4-3 s
    after the previous turn's late subagent.completed.
  3. Observed: read_agent's tool_calls_completed equalled the number of the previous turn's
    tool calls recorded in the lagging stream at that moment (9 of 13).
  4. 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_agent showed idle at 22:07).
    A follow-up asking for more work was sent at 22:08.
  • read_agent at 22:13 and 22:15: status: running, ... total_turns: 1, tool_calls_completed: 22.
  • At 22:37 a second write_agent told it to stop and reply with one word; it returned delivered, task_status=running.
  • At 06:41 the next day the previous turn's subagent.completed was 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>.

  1. 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 reply steer done."
    • B: any trivial task that ends with a text-only reply.
  2. While A is still looping, steer it with write_agent: "Append <HH:mm:ss> ack to
    <scratch>\ack.txt, then continue." ack.txt appears about 12 s after write_agent returns.
  3. Once read_agent shows B idle, send it a follow-up: "Make three separate shell calls running
    Get-Date -Format 'HH:mm:ss', then reply turn1 done plus the times."
  4. 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 reply turn1 done."
  5. Poll read_agent and note when each follow-up's first action happens.
    • Expected: both follow-ups start within seconds, like the steer.
    • Actual: both show running with no new turn, then start after 9.2 min (B) and 6.5 min
      (A), each right after its previous turn's subagent.completed is 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)

  1. Deliver a queued follow-up as soon as the agent is idle (the state read_agent already
    reports), and emit subagent.completed when the turn actually ends.
  2. Report the real state: "queued, not started" (with its age) instead of running, without
    the previous turn's tool-call count.
  3. Add a cancel/stop operation for background agents that does not depend on message delivery.

Related

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/postToolUse hooks configured; they also fire for background-agent tool calls
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:agentsSub-agents, fleet, autopilot, plan mode, background agents, and custom agents

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions