Description
OpenCode CLI's TUI intermittently leaves the terminal in a corrupted state when exiting via /exit while running inside Alacritty + Zellij on Windows. No response/confirmation is needed from the maintainers below — this is a bug report.
Environment
- OS: Windows 11 (10.0.26200)
- Terminal: Alacritty 0.17.0
- Multiplexer:
arndawg.zellij-windows 0.43.1-win32.5 (community fork; zellij has no official Windows support)
- OpenCode version: 1.18.30
- Shell in pane: PowerShell / pwsh
Expected behavior
Exiting via /exit closes OpenCode cleanly and returns to the pane's shell, leaving the Zellij pane usable (and showing the session-reminder message so the session can be resumed). This occasionally happens — the correct behavior — but the result is unpredictable across runs.
Actual (erratic) behavior
Three different outcomes occur non-deterministically:
- Pane closes too: exiting via
/exit sometimes also drops the whole Zellij pane.
- Clean exit: only OpenCode closes; the pane stays alive and usable (expected).
- Corrupted terminal state: before any exit, the app freezes and a series of strange characters appears at the bottom of the pane "as if the terminal were available when it is not". Pressing keys or moving the mouse then emits further gibberish — a mix of special characters, numbers and closing brackets (typical of leaked raw-mode sequences, e.g. raw mouse-report sequences like
ESC[<...M).
Root-cause hypothesis
The TUI renderer is acquired with Effect.acquireRelease(...) and calls destroyRenderer() on release, which exits the alternate screen buffer and restores cooked terminal mode (see packages/tui/src/app.tsx). If that cleanup is bypassed — process.exit(), SIGKILL, or the renderer release being interrupted when the multiplexer tears down the PTY — the terminal remains in raw mode on the alternate screen. Under Windows ConPTY with a second Pty layer (the zellij-windows fork), the mode reset is not propagated to the outer terminal when the process dies uncleanly, so subsequent input/output prints uninterpreted escape sequences.
Mouse reporting is enabled by default (useMouse), which accounts for the numeric/bracket garbage once the terminal state has leaked.
Steps to reproduce
- Launch Alacritty.
- Start
zellij (0.43.1-win32.5).
- In a pane, run
opencode.
- Use the TUI normally, then exit via
/exit.
- Repeat several times (also try closing the pane while OpenCode is running).
Observe the three outcomes above. Once in the corrupted state, any keypress/mouse move prints additional raw escape sequences until the pane is force-closed.
Suggested areas to investigate
- Guarantee
destroyRenderer() runs on every exit path (clean /exit, Ctrl+C, process exit, and external PTY close).
- Consider a fallback (e.g. atexit/signal handler) that force-resets the terminal (
\x1b[0m, leave alternate screen, disable mouse reporting, restore cooked mode) even on abnormal termination.
- Test under a single ConPTY layer (plain Alacritty tab, no zellij) to confirm the multi-Pty ConPTY interaction.
Description
OpenCode CLI's TUI intermittently leaves the terminal in a corrupted state when exiting via
/exitwhile running inside Alacritty + Zellij on Windows. No response/confirmation is needed from the maintainers below — this is a bug report.Environment
arndawg.zellij-windows0.43.1-win32.5 (community fork; zellij has no official Windows support)Expected behavior
Exiting via
/exitcloses OpenCode cleanly and returns to the pane's shell, leaving the Zellij pane usable (and showing the session-reminder message so the session can be resumed). This occasionally happens — the correct behavior — but the result is unpredictable across runs.Actual (erratic) behavior
Three different outcomes occur non-deterministically:
/exitsometimes also drops the whole Zellij pane.ESC[<...M).Root-cause hypothesis
The TUI renderer is acquired with
Effect.acquireRelease(...)and callsdestroyRenderer()on release, which exits the alternate screen buffer and restores cooked terminal mode (seepackages/tui/src/app.tsx). If that cleanup is bypassed —process.exit(), SIGKILL, or the renderer release being interrupted when the multiplexer tears down the PTY — the terminal remains in raw mode on the alternate screen. Under Windows ConPTY with a second Pty layer (the zellij-windows fork), the mode reset is not propagated to the outer terminal when the process dies uncleanly, so subsequent input/output prints uninterpreted escape sequences.Mouse reporting is enabled by default (
useMouse), which accounts for the numeric/bracket garbage once the terminal state has leaked.Steps to reproduce
zellij(0.43.1-win32.5).opencode./exit.Observe the three outcomes above. Once in the corrupted state, any keypress/mouse move prints additional raw escape sequences until the pane is force-closed.
Suggested areas to investigate
destroyRenderer()runs on every exit path (clean/exit, Ctrl+C, process exit, and external PTY close).\x1b[0m, leave alternate screen, disable mouse reporting, restore cooked mode) even on abnormal termination.