Skip to content

TUI output garbled on Windows 11 (v2.8.12) — persists after #11804 and #11789 fixes #12087

@ReemX

Description

@ReemX

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:

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

  1. Large monorepo with ~30+ packages (NestJS backends, Vite React frontends, shared libraries)
  2. dev task uses with: to co-launch 5 persistent tasks (2 backends, 2 frontends, 1 dev-proxy), which depend on ~20 build tasks
  3. Run pnpm dev (triggers turbo watch dev)
  4. 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" merged
  • bashared/backend-auth#buildommand — "@shared/backend-auth#build" + "command" merged
  • / exbase/port-registry#build — exit status fragment + task name merged
  • Ld 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

Suggested Fix Directions

  1. 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!() and warn!() from corrupting the display.
  2. Use synchronized output — Wrap each frame render in BeginSynchronizedUpdate/EndSynchronizedUpdate to prevent partial frame paints on Windows.
  3. Audit and eliminate all eprintln!() calls reachable during TUI mode.

Screenshot

Image

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions