Ctrl-Z then bg lets Codex TUI re-enter raw mode and corrupt the foreground shell
What version of Codex CLI is running?
codex-cli 0.146.0 (standalone x86_64-unknown-linux-musl)
What subscription do you have?
Not relevant; this occurs in local TUI job control.
Which model were you using?
Not model-specific.
What platform is your computer?
Linux x86_64.
What terminal emulator and version are you using (if applicable)?
- tmux 3.6
- zsh
TERM=tmux-256colorTERM_PROGRAM=tmux
codex doctor --summary: 17 ok, 0 warn, 0 fail.
What issue are you seeing?
If Codex is suspended with Ctrl-Z and then explicitly continued as a background job with bg, the resumed TUI attempts to re-enable raw terminal and keyboard modes even though its process group does not own the terminal. This can corrupt the foreground shell's shared TTY.
A visible symptom is staircase output: LF advances to the next row without returning to column zero, consistent with OPOST / ONLCR being disabled:
## branch
M file-one
?? file-two
Recovery requires stty sane (and sometimes a terminal reset).
On a separate isolated zsh/tmux PTY, the same sequence deterministically shows Codex emitting terminal-mode escape sequences immediately after bg, before the kernel stops it again for background TTY access:
[1] continued codex
<keyboard/terminal mode escape sequences>
[1] suspended (tty output) codex
Whether the termios mutation lands before the background-TTY stop appears timing/environment-dependent. It must not be attempted at all while Codex is not the foreground process group.
This is distinct from #29730 and the closed #26564:
- Those issues concern terminal restoration on Ctrl-Z and behavior after
fg. - This issue concerns explicit
Ctrl-Z→bg: Codex is running as a background job while the shell owns the terminal.
Comparable agent TUIs such as Claude Code and OpenCode reportedly support this workflow without corrupting the foreground shell.
What steps can reproduce the bug?
- Start the interactive TUI:
``sh``
codex
- Press Ctrl-Z.
- Continue it in the background:
``sh``
bg
- Run a multiline command in the foreground shell:
``sh``
git status --short --branch
- Observe staircase output or other leaked TUI/raw-mode behavior.
What is the expected behavior?
A background Codex job must never mutate, read from, or render to a terminal owned by another foreground process group.
Ideally, Codex should pause TUI input/rendering while allowing the agent/backend to continue, then restore/redraw when brought back with fg. At minimum, if that architecture is not yet supported, it should safely remain stopped rather than corrupt the shell.
Source-level cause
In rust-v0.146.0, codex-rs/tui/src/tui/job_control.rs resumes terminal handling immediately after SIGCONT:
suspend_process()?;
super::reapply_raw_mode_after_resume()?;
Inside suspend_process(), after the stop returns:
super::terminal_stderr::resume()?;
super::set_modes()?;
There is no check equivalent to:
tcgetpgrp(STDIN_FILENO) == getpgrp()
before set_modes(), raw-mode reapplication, cursor probing, input-buffer flushing, or TUI rendering.
Relevant source:
https://github.com/openai/codex/blob/rust-v0.146.0/codex-rs/tui/src/tui/job_control.rs
Suggested fix
Before any resume-time terminal operation:
- Verify that Codex's process group is the terminal's foreground process group.
- If it is still backgrounded:
- pause terminal input/rendering and terminal-mode changes;
- either keep only non-TTY backend work running, or safely stop again.
- Re-enter raw/TUI modes only after foreground ownership is restored.
- Add a PTY/job-control regression test for
Ctrl-Z→bgasserting the shell retains its original termios flags, especiallyICANON,ECHO,ISIG,OPOST, andONLCR.
Additional information
The separate kill(0, SIGTSTP) versus raise(SIGTSTP) race discussed in #26564 may make the corruption easier to trigger, but replacing that call alone is insufficient for this case: a SIGCONT from bg still must not cause terminal reinitialization without foreground ownership.