Skip to content

1.6.0: wrapper pipes the BEAM's stdout, breaking TTY detection (raw mode, ANSI, stdin reads) for interactive apps #234

Description

@ryanwinchester

Host

Host OS: macOS 26.5.2
Host CPU: aarch64 (Apple Silicon)

zig version: 0.16.0
elixir -v: Elixir 1.20.3 (compiled with Erlang/OTP 29)
Burrito: 1.6.0 (Hex)

Target

Same as host.


Found while packaging a terminal UI app (OTP 28+ raw mode). Related to #215 but independent of it: this reproduces with a fully working ERTS supplied via custom_erts (the workaround from #215). Both problems must be fixed for interactive/TUI apps to work in a Burrito binary.

Cause

The EPIPE fix from #225 makes the Unix wrapper spawn the BEAM with its stdout as a pipe, copied to the real stdout by a thread (src/erlang_launcher.zig, .stdout = .pipe + stdoutCopyThread):

The wrapper now pipes child stdout through itself via a copy thread; when the downstream pipe breaks (EPIPE on write), the copy thread kills the child process

The BEAM therefore sees a pipe on stdout even when the user runs the binary in a terminal. OTP's prim_tty requires both stdin and stdout to be TTYs, so the tty backend is disabled entirely.

Symptoms

Probe run from Application.start/2, binary launched in a real terminal, ERTS = local OTP 29 root via custom_erts (so #215 is out of the picture):

probe stock 1.6.0 (stdout piped) stdout inherited (local patch)
:io_ansi.enabled() (OTP 29) false true
:shell.start_interactive({:noshell, :raw}) {:error, :enotsup} :ok
:io.columns() (after raw) {:error, :enotsup} {:ok, 80}
:io.get_chars(:standard_io, "", 1) never returns — keyboard input is never delivered {:ok, "x"}

Plain writes still pass through the copy thread, so an app can draw — it just can't detect the terminal, enter raw mode, query the size, or read a single keystroke. With stdout inherited, a full TUI (alternate screen, raw keyboard input, mouse reporting) works end to end in the Burrito binary.

Suggested fix

Only interpose the pipe when it's needed: check isatty(STDOUT_FILENO) in the wrapper.

Windows already inherits stdout, so this would also re-unify behavior across platforms. Happy to test a patch.

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