notify hook can corrupt TUI by inheriting stdout/stderr

Resolved 💬 2 comments Opened Jan 23, 2026 by dyxushuai Closed Jan 23, 2026

Summary

When notify is configured, Codex spawns the notifier as a child process. If that notifier writes to stdout/stderr (e.g. Python traceback, logging, echo), it can show up in the same terminal and visibly corrupt the Ratatui TUI.

This is easy to hit when a notifier script fails (network/SSL error, missing env var, etc.) and prints a stack trace.

Repro

  1. Configure notify to something that writes to stderr, e.g.:
notify = ["bash", "-lc", "echo notify-stderr 1>&2"]
  1. Run the Codex TUI and complete a turn.

Observed

The notifier output appears in the terminal and can disrupt the TUI rendering.

Expected

The notifier should not be able to interfere with TUI output.

Root cause

The notifier is started via Command::spawn() and (by default) inherits stdin/stdout/stderr, so any notifier output goes to the same terminal.

Suggested fix

Start the notifier with stdio redirected away from the terminal (or captured to a log file):

  • Default: stdin/stdout/stderr = null
  • Optional: allow debugging via an explicit opt-in (env var or config), e.g. CODEX_NOTIFY_INHERIT_STDIO=1 or notify_inherit_stdio = true.

Pseudo-change in codex-rs/core/src/user_notification.rs:

use std::process::Stdio;
...
command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());

Notes

Even if notify is documented as “best-effort”, the current default makes it very easy for notifier scripts to accidentally break the UX. Redirecting stdio by default seems like a safer baseline for a fullscreen TUI.

Recommended approach

For a fullscreen TUI, the safest default is to prevent the notify child process from ever writing to the terminal:

  • Default: spawn notifier with stdin/stdout/stderr redirected to null (Stdio::null()), so tracebacks/logging can't corrupt the TUI.
  • Debugging: provide an explicit opt-in to inherit stdio (env var like CODEX_NOTIFY_INHERIT_STDIO=1 or a config flag).

This keeps the hook fire-and-forget while making the UX robust even when notifier scripts fail.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗