TUI aborts with SIGABRT (double panic) when stderr is already closed during terminal restore

Open 💬 0 comments Opened Jul 9, 2026 by Eladsafespace

Two SIGABRT crash reports showed up in ~/Library/Logs/DiagnosticReports after codex sessions ended abruptly (in one case the parent process had already exited; in the other, a node parent's pipe was closed). Both crashes have the same shape on two different builds, so this looks like shared teardown code rather than a packaging problem.

What the stacks say happens:

  1. The TUI tears down and codex_tui::TerminalRestoreGuard::restore_silently runs.
  2. It writes to stderr, but stderr is already gone, so std's _eprint panics with the usual "failed printing to stderr".
  3. The panic hook then tries to print that panic message to the same dead stderr, panics again, and the double panic becomes abort().

Crashed thread from the npm build (@openai/codex 0.143.0, aarch64-apple-darwin, image UUID fd137d40-9f13-3914-8d0f-4e79bdaf657d):

  0 libsystem_kernel.dylib     __pthread_kill +8
  1 libsystem_pthread.dylib    pthread_kill +296
  2 libsystem_c.dylib          abort +148
  3 codex                      +0xb077f00
  4 codex                      std::process::abort +12
  5 codex                      std::panicking::panic_with_hook +796
  6 codex                      +0x9099fc8
  7 codex                      std::sys::backtrace::__rust_end_short_backtrace +12
  8 codex                      rust_begin_unwind +32
  9 codex                      core::panicking::panic_fmt +40
 10 codex                      std::io::stdio::_eprint +164
 11 codex                      +0x84428f4
 12 codex                      +0x1146498
 13 codex                      std::panicking::panic_with_hook +616
 14 codex                      +0x9099fc8
 15 codex                      std::sys::backtrace::__rust_end_short_backtrace +12
 16 codex                      rust_begin_unwind +32
 17 codex                      core::panicking::panic_fmt +40
 18 codex                      std::io::stdio::_eprint +164
 19 codex                      codex_tui::TerminalRestoreGuard::restore_silently +76
 20+ (tokio worker frames below)

I have an identical stack from the Codex.app bundled CLI (26.623.141536, image UUID 83e2a45e-1718-37c4-8f97-fb511c389065); only the unsymbolicated offsets differ. Frame 19 is TerminalRestoreGuard::restore_silently in both.

Impact is mostly cosmetic since the process was exiting anyway, but the abort skips whatever cleanup was left. One side effect I can actually measure: sockets under /tmp/codex-browser-use never get unlinked on abrupt teardown, and I accumulated 30+ dead ones within a few days.

I don't have a minimal repro. Closing the terminal tab (or the parent dying) while a TUI session is alive seems to be enough. A function named restore_silently presumably shouldn't be able to panic at all, so ignoring write errors there (and maybe in the panic hook's stderr path) would cover it regardless of how stderr died.

Possibly related: #10248 is the same failure class on stdout during --json streaming.

Environment: macOS 26.5.1 (arm64), codex-cli 0.143.0 (npm) and Codex.app 26.623.141536.

View original on GitHub ↗