You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[Windows] Attach client freezes (blocking WriteConsoleW on a single-threaded runtime) under output backpressure — e.g. selecting/copying in a TUI app #69
On Windows (Windows Terminal + ConPTY), the rmux attach client can freeze completely — the pane stops updating and the keyboard goes dead — while the server stays fully healthy. It is triggered by anything that pauses the terminal's consumption of the ConPTY output pipe; the most reliable trigger is selecting text to copy in a full-screen TUI app (e.g. Claude Code) running inside a pane. In practice the freeze does not recover by clearing the selection, pressing keys, or resizing the window — and even closing the terminal and re-attaching to the same session immediately re-freezes (the pane keeps emitting output that re-blocks the client).
Inside a pane running a TUI app, selecting content + copying freezes that pane.
The rmux server and other sessions stay fully responsive (list-sessions, capture-pane return in tens of ms even under load).
The frozen attach client sits at ~0% CPU (blocked in a syscall, not spinning).
Does not reproduce in a detached session (no client → no console writes).
Root cause (source-level)
The Windows attach client runs read-frames / write-console / forward-keys / resize / detach all on a single current-thread Tokio runtime, and the console write is an inline, synchronous, blocking Win32 call:
single-thread runtime: crates/rmux-ipc/src/stream_windows.rs:107 (Builder::new_current_thread()), driven by runtime.block_on(drive_async_attach(...)) — crates/rmux-client/src/attach_windows.rs:216.
each loop iteration drains + writes before the select!: crates/rmux-client/src/attach_windows/stream.rs:224-225 (output.write_all(&bytes) + output.flush()).
the write is a blocking WriteConsoleW loop until everything is written: crates/rmux-client/src/attach_windows/output.rs:132-162 (Utf16ConsoleWriter::write_text). No spawn_blocking, no dedicated writer thread, no timeout.
So if WriteConsoleW blocks (the terminal stops draining the pipe), the single thread is stuck in the syscall and the runtime cannot advance any other future: it stops reading server frames; the keyboard channel (capacity 256) fills and the keyboard thread's blocking_send blocks → keyboard dead; resize/detach stop. The pane is fully frozen while the server (a separate process) is untouched.
Trigger
Windows consoles pause output while text is selected ("QuickEdit" / selection-pauses-output; cf. microsoft/terminal #34, #4399, #980). That backpressure blocks the client's inline WriteConsoleW.
Aggravating factors:
ENABLE_QUICK_EDIT_MODE is only cleared when the app enables mouse reporting, and left armed otherwise: crates/rmux-client/src/attach_windows/terminal.rs:598-610 (the else branch at :607-609 does not clear it). A TUI app that toggles mouse reporting off (to let the user select natively) re-arms QuickEdit.
rmux answers DECRQM for mode 2026 as "supported" (crates/rmux-core/src/input/dispatch.rs:324-366), and this reply is hardcoded / not gated by terminal-features. That makes apps enable synchronized output and emit full-screen frames; if the client blocks between ?2026h and ?2026l, the terminal stays in an unfinished synchronized batch and the screen looks completely dead (rmux itself warns about this in crates/rmux-core/src/dec_modes.rs:72-75). There is currently no way to disable the 2026 advertisement from config.
What it is NOT
Not unanswered terminal queries: the server answers DSR / DA1 / DA2 / DECRQM / DECRQSS / XDA correctly, independent of attach (crates/rmux-core/src/input/dispatch.rs, replies written back via crates/rmux-server/src/pane_io/reader.rs:467).
Not clipboard config (set-clipboard, copy-command).
This is the same class as the already-fixed server-side single-thread starvation (#34), but moved to the Windows attach client and worse: a hard blocking syscall rather than CPU starvation.
Reproduction
Requires a real console + interactive selection (cannot be scripted — a non-interactive spawned client gets a pipe, not a console, and never reaches the blocking WriteConsoleW):
Windows Terminal → rmux new -s t → run a full-screen TUI app that uses synchronized output (e.g. Claude Code).
While the app is actively rendering, select text in the terminal (mouse drag, or the app's own copy that toggles mouse mode).
The pane freezes; the server stays responsive. Clearing the selection / resizing / closing + re-attaching does not reliably recover.
Unconditionally clear ENABLE_QUICK_EDIT_MODE in raw_input_mode_with_mouse, not only when the app enables mouse reporting.
(Optional) Gate the DECRQM-2026 "supported" reply behind a config / terminal-features option so synchronized output can be disabled server-side as a mitigation.
Summary
On Windows (Windows Terminal + ConPTY), the
rmux attachclient can freeze completely — the pane stops updating and the keyboard goes dead — while the server stays fully healthy. It is triggered by anything that pauses the terminal's consumption of the ConPTY output pipe; the most reliable trigger is selecting text to copy in a full-screen TUI app (e.g. Claude Code) running inside a pane. In practice the freeze does not recover by clearing the selection, pressing keys, or resizing the window — and even closing the terminal and re-attaching to the same session immediately re-freezes (the pane keeps emitting output that re-blocks the client).Environment
Symptoms
list-sessions,capture-panereturn in tens of ms even under load).Root cause (source-level)
The Windows attach client runs read-frames / write-console / forward-keys / resize / detach all on a single current-thread Tokio runtime, and the console write is an inline, synchronous, blocking Win32 call:
crates/rmux-ipc/src/stream_windows.rs:107(Builder::new_current_thread()), driven byruntime.block_on(drive_async_attach(...))—crates/rmux-client/src/attach_windows.rs:216.select!:crates/rmux-client/src/attach_windows/stream.rs:224-225(output.write_all(&bytes)+output.flush()).WriteConsoleWloop until everything is written:crates/rmux-client/src/attach_windows/output.rs:132-162(Utf16ConsoleWriter::write_text). Nospawn_blocking, no dedicated writer thread, no timeout.So if
WriteConsoleWblocks (the terminal stops draining the pipe), the single thread is stuck in the syscall and the runtime cannot advance any other future: it stops reading server frames; the keyboard channel (capacity 256) fills and the keyboard thread'sblocking_sendblocks → keyboard dead; resize/detach stop. The pane is fully frozen while the server (a separate process) is untouched.Trigger
Windows consoles pause output while text is selected ("QuickEdit" / selection-pauses-output; cf. microsoft/terminal #34, #4399, #980). That backpressure blocks the client's inline
WriteConsoleW.Aggravating factors:
ENABLE_QUICK_EDIT_MODEis only cleared when the app enables mouse reporting, and left armed otherwise:crates/rmux-client/src/attach_windows/terminal.rs:598-610(theelsebranch at :607-609 does not clear it). A TUI app that toggles mouse reporting off (to let the user select natively) re-arms QuickEdit.crates/rmux-core/src/input/dispatch.rs:324-366), and this reply is hardcoded / not gated byterminal-features. That makes apps enable synchronized output and emit full-screen frames; if the client blocks between?2026hand?2026l, the terminal stays in an unfinished synchronized batch and the screen looks completely dead (rmux itself warns about this incrates/rmux-core/src/dec_modes.rs:72-75). There is currently no way to disable the 2026 advertisement from config.What it is NOT
crates/rmux-core/src/input/dispatch.rs, replies written back viacrates/rmux-server/src/pane_io/reader.rs:467).set-clipboard,copy-command).This is the same class as the already-fixed server-side single-thread starvation (#34), but moved to the Windows attach client and worse: a hard blocking syscall rather than CPU starvation.
Reproduction
Requires a real console + interactive selection (cannot be scripted — a non-interactive spawned client gets a pipe, not a console, and never reaches the blocking
WriteConsoleW):rmux new -s t→ run a full-screen TUI app that uses synchronized output (e.g. Claude Code).Suggested fix
WriteConsoleWintokio::task::spawn_blocking, or use a dedicated writer thread + bounded channel, or run the attach client on a multi-threaded runtime. (Primary fix — same root as Daemon freezes when one pane produces heavy output (single-threaded tokio runtime on Unix) #34.)ENABLE_QUICK_EDIT_MODEinraw_input_mode_with_mouse, not only when the app enables mouse reporting.terminal-featuresoption so synchronized output can be disabled server-side as a mitigation.Key source references
crates/rmux-ipc/src/stream_windows.rs:107·crates/rmux-client/src/attach_windows.rs:216·crates/rmux-client/src/attach_windows/stream.rs:224-225·crates/rmux-client/src/attach_windows/output.rs:132-162·crates/rmux-client/src/attach_windows/terminal.rs:598-610·crates/rmux-core/src/input/dispatch.rs:324-366·crates/rmux-core/src/dec_modes.rs:72-75