TUI wedges mid-turn (screen clears, input dead, Ctrl+C/Ctrl+\ ignored) — write EIO on stdout followed by EPIPE on Rust JSON-RPC transport; WSL2 + Windows Terminal
- Dominant language
- Shell
- Stars
- 11.2k
- Forks
- 1.9k
- Avg merge
- 14h 16m
- Merged PRs (30d)
- 6
Description
# Copilot CLI — mid-turn screen clear / unresponsive terminal
## Environment
- CLI version: `1.0.70-0`
- Node: `v24.11.1`
- OS: WSL2 (Ubuntu) on Windows 11
- Terminal: Windows Terminal
- Model in use: `claude-opus-4.7`
## Symptom
While the assistant is actively working in a session (streaming a reply and/or
running tools mid-turn), the CLI's terminal pane suddenly:
1. Clears to a blank alternate-screen buffer (just a cursor at column 0)
2. Stops rendering any further output
3. Stops accepting input — typing does nothing
4. Ignores `Ctrl+C` **and** `Ctrl+\` (SIGQUIT)
5. The underlying `copilot` node process remains alive; only tab-close /
external `kill -9` from another shell (or a reboot) recovers the terminal
Not idle-related. Reproduced within ~10 minutes of a fresh boot, mid-turn.
Multiple sessions in the same day exhibited it.
## What is NOT happening
- Not caused by leaving the CLI idle (initially suspected, ruled out — happens
mid-turn during active streaming/tool use).
- Not a terminal resize (no SIGWINCH in the timeline).
- Not user-initiated shutdown (no `Ctrl+D`, no `/exit`, no window close).
## Log signature
Recent process logs under `~/.copilot/logs/` for wedged sessions contain this
pattern around the point of failure:
```
[DEBUG] Ignoring transient stdout error: write EIO
[DEBUG] Uncaught Exception (transient I/O, suppressed): write EPIPE
Error: write EPIPE
[ERROR] Rust JSON-RPC transport error: Error: write EPIPE
...
[DEBUG] [shutdown] Starting dispose: InkInstance.rerender
[DEBUG] Ignoring transient stdout error: write EIO
[DEBUG] [shutdown] Completed dispose: InkInstance.rerender (65ms)
```
The `write EIO` on stdout and `write EPIPE` on the Rust JSON-RPC transport
cluster together, then Ink attempts a shutdown re-render. This looks like the
Ink renderer (or something writing to stdout in parallel with it) hits EIO,
which then cascades EPIPE across the extension-host IPC pipes. Because the
CLI classifies EIO as "transient" and does not abort, the process keeps
running with a dead TTY — no input handler, no visible output.
Contributing hypothesis: high re-render rate during streaming
(`assistant.streaming_delta` + `assistant.tool_call_delta` events) combined
with concurrent writers to stdout produces a short window where a write races
with something (terminal state change? SIGWINCH from Windows Terminal?
extension-host handshake?) and stdout returns EIO. Once stdout is EIO, Ink
can no longer paint and the input reader also stalls.
## Wedged process observations
Example: PID 2160 (`copilot`, pts/2), state `Sl+`, still alive over an hour
after the wedge. Log
`~/.copilot/logs/process-1783606860101-2160.log` shows normal activity, then
only ExP telemetry heartbeats every ~10 minutes — no user input events, no
render updates. Consistent with "TUI/input loop dead, background workers
alive."
## Impact
- Loss of the active turn's visible output (state is still on disk under
`~/.copilot/session-state//` and resumable, but the in-progress reply
is gone).
- Ctrl+C / Ctrl+\ ineffective — no graceful shutdown path from within the
wedged terminal.
- Requires killing the process from a separate shell, or a system reboot in
the worst case.
## Requested fixes / improvements
1. When stdout returns `EIO` repeatedly (say, more than N times in M ms),
treat it as fatal instead of transient. Either:
- attempt to reopen `/dev/tty` and re-attach Ink, or
- exit cleanly with a diagnostic message.
2. Ensure `Ctrl+C` and `Ctrl+\` remain honored even if the Ink render loop
is stalled — the SIGINT/SIGQUIT handler should not depend on the TUI
being alive.
3. Optional: a `COPILOT_NO_ALT_SCREEN=1` / `--no-alt-screen` escape hatch
that renders inline instead of using the alternate screen buffer, so a
wedge doesn't hide scrollback and leaves recovery affordances visible.
4. Investigate whether the extension-host EPIPE bursts (link-checker,
stale-content-finder, pr-review-feedback, crlf-guard, frontmatter-validator,
ms-date-auto-updater are all forked at startup on this system) contribute
by writing to shared stdio pipes during a render.
Contributor guide
Research direction
Start by reproducing the mid-turn wedge in WSL2 with Windows Terminal while collecting the ~/.copilot/logs/ process log, then trace the stdout EIO handling, Ink shutdown re-render, and Rust JSON-RPC EPIPE sequence described here. Done should include a defined recovery or clean-exit behavior, with Ctrl+C and Ctrl+\\ still effective when rendering is stalled.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- node.js, rust
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Needs clarification
- Newbie friendliness
- 35/100