-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Description
Description
The TUI output is severely garbled on Windows 11 when running turbo watch dev in a large monorepo (~30+ packages). Task names merge together, "command ... exited" status messages leak between panes, and partial file paths appear scattered across the sidebar area.
This occurs on v2.8.12, which includes both fixes from:
- PR fix: Normalize bare LF to CRLF in TUI output to prevent garbled logs #11804 (normalize bare LF → CRLF) — shipped in v2.8.7
- PR fix: Emit space for empty vt100 cells to fix TUI repaint on task switch #11789 (emit spaces for empty vt100 cells) — shipped in v2.8.7
Those fixes addressed #9057 but did not resolve the Windows rendering corruption. The same monorepo renders cleanly on Linux.
Environment
- OS: Windows 11 Pro, Build 26200.7840 (x86_64)
- Terminal: PowerShell in Windows Terminal, also reproduces in VSCode integrated terminal
- Turborepo: 2.8.12
- Node.js: v22.18.0
- Package Manager: pnpm 10.30.3
- turbo.json UI:
"ui": "tui"
Reproduction
- Large monorepo with ~30+ packages (NestJS backends, Vite React frontends, shared libraries)
devtask useswith:to co-launch 5 persistent tasks (2 backends, 2 frontends, 1 dev-proxy), which depend on ~20buildtasks- Run
pnpm dev(triggersturbo watch dev) - Observe garbled output during the build phase and when dev servers start/fail
The corruption is worst during the initial burst when 20+ build tasks run concurrently before the persistent dev tasks start.
Observed Output
Instead of clean task labels and organized panes:
adjutant-frontend#dev
@base/dev-proxy#dev
authenticator-frontend#dev
adjutant-backend#devuild
authrnticator-backend#dev
@shared/bootstrap#build
bashared/backend-auth#buildommand (C:\Users\reema\Documents\Programming\the-best-org\packages\base\port-registry) C:\Users\reema\Documents\Programming\the-best-org\node_modules\.bin\pnpm.CMD run bui
/ exbase/port-registry#build
@shared/backend-auth#build: command (...) C:\...\pnpm.CMD run bui
Ld exited (1)onstants#build
@shared/schemas#build
@shared/swagger#build
@shared/utils#build
Key corruption patterns:
adjutant-backend#devuild— "dev" + "build" mergedbashared/backend-auth#buildommand— "@shared/backend-auth#build" + "command" merged/ exbase/port-registry#build— exit status fragment + task name mergedLd exited (1)onstants#build— "exited (1)" merged with "@shared/constants#build"
Later in the output:
or-backend#dev: command (...) C:\...\node_module
rs\reema\Documents\Programming\...\apps\adjutant\backend) ... exited (1)
@authenticator/schemas#buildand (...) ... exited (1)
@base/dev-proxy#dev: command (C:\Users\reema\Documents\Programming\the-best-org\packages\base\dev-proxy) C:\Users\reema\Documents\Programming\the-best-org\node_modules\.bin\pnpm.CMD run dev exited (1)
Interspersed with bare 0 characters, lone » symbols, and orphaned > prompts.
Interacting with the TUI (switching panes, scrolling) partially "heals" the display, but new corruption appears on the next output cycle.
What Works
- Linux: Same monorepo, same turbo.json, clean TUI output
"ui": "stream"on Windows: Clean output (no TUI)
Analysis
After studying the codebase, the TUI architecture has proper per-task isolation (each task has its own TerminalOutput with independent vt100::Parser and Screen). The normalize_newlines() fix handles bare LF, and the empty cell fix prevents stale frame content. These are correct and necessary, but insufficient for Windows.
Suspected remaining causes:
1. tracing::error!() writes directly to stderr during TUI rendering
In crates/turborepo-task-executor/src/exec.rs, multiple error!() macro calls (lines 510, 513, 548, 551, 577, 580) write to stderr via StdErrWrapper (which goes to std::io::stderr()). On Windows, stderr output is not redirected to the alternate screen — it writes directly to the underlying console buffer, corrupting the TUI display. On Linux, stderr and the alternate screen coexist more gracefully due to how POSIX PTYs handle output streams.
2. Direct eprintln!() calls during task graph execution
crates/turborepo-lib/src/task_graph/visitor/mod.rs:530-532 has bare eprintln!() calls that write directly to stderr while the TUI may be active.
3. ConPTY buffering/timing differences
Windows ConPTY has well-documented quirks with rapid cursor repositioning. When 20+ tasks produce output simultaneously and the TUI redraws at 3ms intervals (FRAMERATE), ConPTY's buffering may cause escape sequences to be split across reads or interleave with other output in ways that don't occur with POSIX PTYs.
4. No SynchronizedOutput / buffer sync
The ratatui rendering pipeline doesn't use crossterm's BeginSynchronizedUpdate/EndSynchronizedUpdate. On Linux terminals, partial frame updates are less visible because terminal emulators are fast. On Windows Terminal + ConPTY, partial frame paints are more likely to be visible during the ~3ms render cycle, especially when many tasks are producing output simultaneously.
Related Issues
- TUI often garbled by errors in tasks #9057 — TUI garbled by errors (fixed by fix: Normalize bare LF to CRLF in TUI output to prevent garbled logs #11804 + fix: Emit space for empty vt100 cells to fix TUI repaint on task switch #11789, but not fully resolved on Windows)
- Terminating TUI with ctrl+c on Windows causes terminal to act weird #8860 — Ctrl+C on Windows corrupts terminal state (open)
- [turborepo] Windows VSCode terminal issues with Turbo UI #7808 — Windows VSCode terminal issues with TUI (open)
- TUI fails on Windows with "Initial console modes not set" in v2.7.4 #11434 — "Initial console modes not set" (fixed v2.7.5)
- pnpm dev hangs in turborepo-basic with turbo ^2.8.7 (TUI opens, no tasks start) #11808 — TUI hangs on Windows, conhost.exe 100% CPU (fixed)
- chore(tui): redirect tracing output to files during tui usage #9003 — Attempted redirect of tracing output during TUI (closed, incomplete)
Suggested Fix Directions
- Redirect tracing to a file (or suppress) during TUI mode — PR chore(tui): redirect tracing output to files during tui usage #9003 attempted this but was abandoned. Completing this would prevent
error!()andwarn!()from corrupting the display. - Use synchronized output — Wrap each frame render in
BeginSynchronizedUpdate/EndSynchronizedUpdateto prevent partial frame paints on Windows. - Audit and eliminate all
eprintln!()calls reachable during TUI mode.
Screenshot
