Skip to content

Terminal UI stops consuming events (input + scroll dead) when a turn spawns parallel subagents; runtime keeps running #4533

Open

Description

@bikramjitk

Summary

On the prerelease channel (1.0.81-4, 1.0.81-5), the terminal UI stops
consuming runtime events at the moment a turn launches a parallel block of
subagents
. The Rust runtime is unaffected and keeps working — subagents
continue making model calls for minutes afterwards and their results are
persisted to the session store — but the terminal never repaints again, accepts
no keyboard input, and scrollback is frozen.

The process is left idle, not spinning: 0 CPU ticks over a 5s sample,
State: S (sleeping), wchan: futex_wait_queue_me, with no busy child
processes. It never recovers and does not exit on its own.

Reproduced 3 times in one evening across independent sessions.

Environment

  • Copilot CLI 1.0.81-5 (also seen on 1.0.81-4) — prerelease channel
  • Node.js v22.21.1, Linux x64
  • Several MCP servers configured (stdio + http)

Steps to reproduce

  1. Run a workflow where the top-level agent invokes a custom agent via task.
  2. Have that custom agent fan out to 2–3 subagents of its own in a single
    parallel block
    (i.e. multiple task calls issued in one turn).
  3. Observe the terminal at the moment the parallel block is launched.

Total concurrency at failure was 3–4 simultaneous subagents. Sequential
subagent execution has not reproduced it.

Evidence

1. The UI log stops at a turn boundary, always with the same event triple.
The final lines in every frozen session:

[WARNING] useTimeline: skipping unprocessable event type=model.response: Error: Unhandled event type in timeline: model.response
[WARNING] useTimeline: skipping unprocessable event type=model.turn_ended: Error: Unhandled event type in timeline: model.turn_ended
[WARNING] useTimeline: skipping unprocessable event type=model.messages_snapshot: Error: Unhandled event type in timeline: model.messages_snapshot

2. The timeline reducer cannot handle any model.* event. These warnings
are continuous throughout normal operation, thousands per session — one per
runtime event. Counts scale directly with subagent usage (0–260 in ordinary
sessions; 2,504 in a heavy multi-agent session). This looks like event types
emitted by the runtime that the TUI timeline reducer does not know about, i.e.
a skew between the runtime and the UI layer within the same build.

Observed types: model.message, model.turn_started, model.turn_ended,
model.model_call_started, model.model_call_success,
model.captured_assignment_context, model.tool_execution, model.response,
model.messages_snapshot.

3. The runtime continues after the UI dies. Per-call usage rows recorded in
the local session store, cross-referenced with the last UI log line:

Session Last UI event Runtime kept working until Peak concurrent subagents
A T+0 — 3 (reached that same minute)
B T+0 T+8min 4
C T+0 T+2min or more 4 (fan-out began T+1min)

In session B the subagents ran to completion and their results were persisted;
only the UI was lost. copilot --resume <session-id> recovers the session,
which confirms the runtime state is intact.

Expected

The UI keeps rendering and accepting input while subagents run in parallel, or
at minimum fails loudly rather than silently detaching from the event stream.

Actual

UI silently stops consuming events. Terminal appears hung; the only recovery is
killing the process and resuming the session.

Secondary issue: frozen sessions ignore SIGTERM and can leave a spinning zombie

  1. All four affected processes ignored SIGTERM and required SIGKILL.
  2. One process that had begun disposal never exited, logging this once per
    second indefinitely (grew its log to 656 KB before it was killed):
[ERROR] SessionClient poll loop error (will retry): Error: Cannot invoke native session after disposal has started
[ERROR] SessionClient background task refresh failed (continuing) [phase=pre-dispatch cursor=… attempts=2

Over one session these accumulated to 10 simultaneous long-lived CLI processes,
none of which could be shut down normally.

Workaround

Force subagents to run sequentially rather than in parallel blocks, and pin to
the stable channel (COPILOT_AUTO_UPDATE=false with a stable version installed
via npm).

Activity

  1. added
    area:agentsSub-agents, fleet, autopilot, plan mode, background agents, and custom agents
    area:terminal-renderingDisplay and rendering: flickering, scrolling, line wrapping, output formatting
    and removed on Aug 20, 2026
  2. MilkyWay008 commented on Aug 21, 2026

    @MilkyWay008

    The useTimeline: skipping unprocessable event type=model.response warnings are the real tell, the reducer just doesn't have a branch for any of the model.* events so the whole timeline pipeline stalls when a parallel block kicks off. Runtime keeps working, only the UI detaches. Until a patch lands, avoiding parallel subagent blocks or dropping back to a release build dodges it, and you'll want SIGKILL since the frozen process ignores the polite one.

  3. bullmastiffo commented on Aug 26, 2026

    @bullmastiffo

    Environment

    Copilot CLI: 1.0.81-3
    Agency:      2026.8.25.2 installed
    OS:          WSL2 Linux x86_64
    Kernel:      6.18.35.2-microsoft-standard-WSL2
    tmux:        3.4
    Shell:       bash
    Mode:        interactive TUI, resumed session
    Recovery:    process-group termination followed by --resume
    

    I can reproduce this repeatedly with Copilot CLI 1.0.81-3 on WSL2 Linux
    x86_64 under tmux 3.4. A resumed interactive session becomes permanently
    stuck on "Working" while waiting for a task/background subagent.

    This is a true hang, not only a misleading status indicator:

    • Pressing Esc does not interrupt or stop the agent.
    • Pressing Esc twice does not stop background agents or return the prompt.
    • Pressing Ctrl-C once does not cancel the turn.
    • Repeated Ctrl-C does not reliably exit or recover the UI.
    • The terminal and tmux pane remain alive and connected to the correct
      foreground process group.
    • The Copilot process remains alive but sleeps in futex_do_wait.
    • Its accumulated process CPU time does not advance across repeated samples.
    • Two pane captures taken ten seconds apart are byte-for-byte identical.
    • No task, test, or shell child remains active; only MCP proxy children remain.
    • Several prior child processes are present as unreaped zombies.
    • The session events.jsonl stopped updating immediately after session.resume,
      despite substantial later activity still being visible in the TUI.
    • The Copilot process log later stopped updating entirely while the outer
      wrapper app continued recording successful keepalives.
  4. bullmastiffo commented on Aug 26, 2026

    @bullmastiffo

    it's starting to be unusable :(

    Report: 2026-08-26 fresh-session reproduction

    This is a second, independently observed hang on the same day. Unlike the
    earlier occurrence, this one began in a fresh interactive session rather than
    a resumed session. No prompt text, task arguments, repository information, or
    private paths are included below.

    Environment

    Copilot CLI:    1.0.81-3
    Custom wrapper: 2026.8.25.6
    OS:             Ubuntu 22.04 under WSL2, Linux x86_64
    Kernel:         6.18.35.2-microsoft-standard-WSL2
    Host terminal:  Windows Terminal
    Terminal layer: tmux 3.4
    Shell:          bash
    Locale:         C.UTF-8
    Session mode:   fresh interactive TUI session
    

    The affected pane remained attached to the correct foreground process group.
    tmux remained responsive from other panes, and the custom wrapper continued
    successful session keepalives after Copilot stopped producing events or logs.

    Timeline

    All local timestamps are CEST (UTC+2).

    Time Observation
    13:45:53 Fresh Copilot session started.
    14:48:27 First task tool and subagent lifecycle started. Neither later received a terminal event.
    14:58:59 Second task tool and subagent lifecycle started. Neither later received a terminal event.
    14:58:59 Response WebSocket opened.
    14:59:00 Response WebSocket connected successfully.
    14:59:05-14:59:21 Model responses continued to arrive.
    14:59:22.005 Final assistant.message event.
    14:59:22.191 Final tool.execution_complete event, for an unrelated completed tool call.
    14:59:26.734 Final Copilot process-log entry.
    18:14:20 or later Custom-wrapper keepalives were still succeeding.

    Copilot's event journal and process log therefore became silent within about
    four seconds of each other, while the outer process remained healthy for more
    than three additional hours.

    Missing lifecycle events

    The event journal contained 119 unique tool.execution_start records but only
    117 unique tool.execution_complete records. It contained four
    subagent.started records but only two terminal subagent.completed or
    subagent.failed records.

    Redacted operation Start time (UTC) Events present Events missing
    Task A (call_jHpt...64uu) 12:48:27.181 tool.execution_start, subagent.started tool.execution_complete, subagent.completed/failed
    Task B (call_Aqmu...JtNv) 12:58:59.669 tool.execution_start, subagent.started tool.execution_complete, subagent.completed/failed

    No task arguments, descriptions, prompt text, or subagent output were retained
    in this report.

    Process snapshot during the hang

    Copilot process state:       sleeping
    Linux wait channel:          futex_do_wait
    Threads:                     39
    Resident memory:             approximately 918 MiB
    Accumulated CPU time:        00:08:28
    CPU-time change over 10 s:   0
    Open socket states:          3 established, 1 listening
    Available system memory:     approximately 13 GiB
    Active process-group child:  1 MCP helper
    Zombie wrapper children:     17
    

    CPU time, write I/O, and voluntary/involuntary context-switch counts did not
    advance across three samples. Only a small amount of read accounting changed,
    consistent with passive pipe or socket activity. Two pane captures taken 15
    seconds apart had identical hashes. Esc, double-Esc, and Ctrl-C did not
    interrupt the operation or restore the prompt.

    This was not CPU saturation or memory exhaustion. Network connectivity was not
    generally lost: three connections remained established, and the response
    WebSocket had connected successfully immediately before the final operation.

    Log and journal state

    Event journal:       764 lines, 5,524,917 bytes
    Event journal mtime: 2026-08-26 14:59:22 CEST
    Copilot process log: 23,218,883 bytes
    Process log mtime:   2026-08-26 14:59:26 CEST
    Wrapper log:         continued successful keepalives through at least 18:14 CEST
    

    There was no panic, HTTP 429/rate-limit response, HTTP 5xx response, timeout
    error, or WebSocket disconnect at the hang boundary. Earlier normal
    WebSocket-idle closures had recovered successfully.

    Two startup-only diagnostics were present:

    • A task API lookup returned HTTP 404 when the fresh session started.
    • A local custom-agent Markdown file had malformed frontmatter.

    Both occurred before the hang boundary; neither coincided with the event/log
    cutoff. Some successful startup and IDE-connection messages were emitted at
    error severity, so severity alone was not used to classify failures.

    Assessment

    The strongest signature is two subagents that started without completion or
    failure events, paired with two parent task calls that started without tool
    completion events. Copilot then stopped writing both its event journal and
    process log while sleeping indefinitely in a futex wait. The terminal,
    foreground process group, sockets, tmux server, and outer custom wrapper all
    remained alive.

    This is consistent with an internal task-completion/event-consumption stall or
    deadlock rather than a tmux input or rendering issue. It most closely resembles
    GitHub issues #4225 and #4533. The unreaped custom-wrapper children may also be
    relevant to #4385, although the missing lifecycle events are the more direct
    correlation in this reproduction.

  5. hcoona commented on Aug 28, 2026

    @hcoona

    Additional RCA: subagent completion is lost after a two-choice stop response

    I captured another hang after resuming a persisted session on Copilot CLI 1.0.81-3 with debug logging enabled. This occurrence did not use /restart after the resume. It appears related to this issue, but the evidence places the failure below the terminal renderer: the model response reaches the native runtime, and the runtime then fails to emit the terminal lifecycle events required to unwind the nested task chain.

    Trigger and control case

    The top-level agent dispatched a custom agent, which led to nested task dispatches reaching the enforced maximum subagent depth of four. Attempts to exceed that depth returned the expected Maximum sub-agent depth of 4 reached error.

    Two sibling subagents then diverged:

    • The first returned one stop choice with phase: final_answer. The runtime emitted assistant.message, subagent.completed, and the parent tool.execution_complete normally.
    • The second returned two stop choices in the same model response:
    Choice Content finish_reason phase responses_message_status
    0 Empty stop Absent Absent
    1 Nonempty final answer stop final_answer completed

    Failure boundary

    The [rust:model_wire] log recorded the complete two-choice response at 2026-08-28T09:36:00.961Z. After that boundary, the log contained four assistant_reasoning telemetry records but none of the following:

    • assistant.message
    • subagent.completed or subagent.failed
    • the parent tool.execution_complete

    The persisted event journal had already stopped at 2026-08-28T09:35:52.562Z. Every ancestor remained blocked waiting for the missing child completion. The process stayed alive but became quiescent: CPU time stopped advancing, and all threads were waiting in futex/epoll paths. SIGTERM did not terminate it; recovery required SIGKILL followed by resuming the persisted session.

    Assessment

    This narrows the likely defect to the native model-response-to-subagent-completion path. An empty terminal choice preceding a valid completed final_answer choice appears to leave the subagent without a terminal state. Reaching the depth limit may have contributed to producing this response shape, but the available evidence does not establish that the depth limit is required.

    The runtime should either select the valid completed final-answer choice or reject the response explicitly. In either case, every terminal model-response path should emit exactly one subagent.completed or subagent.failed event and complete the parent task call so its ancestors can unwind.

    Environment

    Copilot CLI: 1.0.81-3
    OS: Microsoft Azure Linux 3.0 under WSL2
    Kernel: Linux 6.6.114.1-microsoft-standard-WSL2
    Architecture: x86_64
    Terminal layer: tmux 3.7b
    Shell: zsh 5.9
    Session: resumed interactive session; no /restart after resume
    

    I have not attached the raw logs because they contain prompt and repository context. The structural fields above were extracted directly from the captured response and event journal; I can provide a further-redacted excerpt if needed.

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 agentsarea:terminal-renderingDisplay and rendering: flickering, scrolling, line wrapping, output formatting

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions