#2742·ratatui

Blocking `flush()` on a full pty freezes the entire render loop, and there is no non-blocking / async escape hatch

Author: vpelikhCreated Aug 30, 2026Updated Sep 3, 2026
LabelsType: Bug

Description

Terminal::draw() / Terminal::flush() writes synchronously to fd 1 (the pty). When the terminal emulator stops draining the pty's output buffer (a backgrounded tab, a dead multiplexer pane, a frozen SSH session), that buffer fills and the next write() blocks forever.

The entire interactive TUI runs on a single render thread (tokio::block_on). When that thread blocks on a pty write, the UI freezes completely: cursor, input, animations, and scroll stop.

This is the "TUI stuck" failure mode.

To Reproduce

  1. Run a fullscreen TUI that renders on a single blocking thread.
  2. Background or disconnect the terminal so the pty output buffer is not drained (e.g. kill -STOP the terminal emulator, or detach a tmux pane whose parent is blocked).
  3. Trigger any frame larger than the kernel pty buffer (~a few KB on most systems).
  4. The render thread parks in the kernel on write() and never returns; the UI is frozen.

A minimal reproduction: build Terminal<CrosstermBackend<Stdout>>, put the writer into a state where the pty buffer is full, then call terminal.draw(...) with a frame larger than the available buffer. The call blocks indefinitely.

Expected behavior

A TUI should be able to render without risking a hard, unrecoverable thread block on a wedged pty. The render loop must be able to detect a stall and exit cleanly while keeping the live server session resumable.

Environment

  • OS: macOS / Linux
  • Terminal Emulator: kitty, ghostty, Apple Terminal, tmux, etc.
  • Crate version: ratatui 0.30.0
  • Backend: crossterm

Additional context

This issue is distinct from ratatui #2483 / PR #2485 (CPR cursor-query racing with stdin on resize). That problem was about get_cursor_position() calls blocking on stdin replies during draw(). This issue is about flush() blocking on a full pty buffer.