TUI: first-run trust dialog drops the ENTER sent as soon as it renders (0.148.0, 0.149.0-alpha.1; 0.147.0 accepts it)

Open
#39,487 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
65/100
Issue type
Bug
Clarity
Mostly clear
Activity status
Quiet
Tech stack
rust
Domain
cli

Research direction

Start by running codex_trust_dialog_probe.py against 0.147.0 and 0.148.0 with the enter-now strategy to reproduce the regression, then inspect the TUI trust-dialog startup and input handling. Done means ENTER sent when “Press enter to continue” first appears advances past the dialog without a delay, while the existing delayed-enter behavior remains working.

Written by the indexing model from the issue text.

Description

bug CLI TUI
What happened

Since 0.148.0, an ENTER written to the pty as soon as the first-run
directory-trust screen renders (Press enter to continue) is dropped. The
screen stays up and the session never reaches the composer.

0.147.0 accepts the same keystroke at the same moment. 0.149.0-alpha.1
still drops it, so this is not fixed on the alpha line.

This breaks every non-interactive driver of the TUI — terminal-recording
harnesses, CI smoke tests, expect-style scripts — because "the marker is on
screen" is the only signal such a driver has that the dialog is ready for an
answer.

Reproduction

No wrapper, no config, no network: a bare codex in a pty under a throwaway
HOME, with ENTER written the instant Press enter to continue first
appears.

"""Save as codex_trust_dialog_probe.py; run: python3 codex_trust_dialog_probe.py 0.148.0 enter-now"""
import fcntl, os, pty, re, select, struct, subprocess, sys, tempfile, termios, time

STRATEGIES = {
    "enter-now": [(0.0, b"\r")],
    "enter-after-3s": [(3.0, b"\r")],
}
MARKER = b"Press enter to continue"

version = sys.argv[1]
strategy = sys.argv[2] if len(sys.argv) > 2 else "enter-now"
prefix = f"/tmp/codex-versions/{version}"
binary = f"{prefix}/node_modules/.bin/codex"
if not os.path.exists(binary):
    os.makedirs(prefix, exist_ok=True)
    subprocess.run(["npm", "install", "--prefix", prefix, f"@openai/codex@{version}"], check=True)

home, work = tempfile.mkdtemp(), tempfile.mkdtemp()
pid, fd = pty.fork()
if pid == 0:
    os.chdir(work)
    os.environ["TERM"] = "xterm-256color"
    os.environ["HOME"] = home
    os.execv("/bin/sh", ["/bin/sh", "-c", binary])
# pty.fork() leaves the slave at 0x0 and codex renders nothing into it.
fcntl.ioctl(fd, termios.TIOCSWINSZ, struct.pack("HHHH", 30, 80, 0, 0))

pending = list(STRATEGIES[strategy])
buffer, marker_at, started = b"", None, time.time()
while time.time() - started < 20:
    readable, _, _ = select.select([fd], [], [], 0.1)
    if readable:
        chunk = os.read(fd, 65536)
        if not chunk:
            break
        buffer += chunk
    if marker_at is None and MARKER in buffer:
        marker_at = time.time()
    if marker_at is not None and pending and time.time() - marker_at >= pending[0][0]:
        os.write(fd, pending.pop(0)[1])

plain = re.sub(r"\x1b\[[0-9;?>< ]*[a-zA-Z]", "", buffer.decode("utf8", "replace")).replace("\x1b", "")
print(f"codex {version} strategy={strategy} bytes={len(buffer)} "
      f"still_on_dialog={MARKER.decode() in plain[-600:]}")
os.kill(pid, 15)
Results
version keystroke bytes written to the pty still on the trust dialog after 20 s
0.147.0 ENTER at marker + 0 s 5 986 no
0.147.0 ENTER at marker + 3 s 27 838 no
0.148.0 ENTER at marker + 0 s 141 221 yes
0.148.0 ENTER at marker + 3 s 28 445 no
0.149.0-alpha.1 ENTER at marker + 0 s 9 680 yes
0.149.0-alpha.1 ENTER at marker + 3 s 4 026 no

Two separate observations:

  1. The keystroke is lost. In every still_on_dialog=yes row the write to
    the pty succeeded; the dialog simply did not advance. Delaying the same
    single ENTER by 3 s is enough for it to be accepted, so the dialog is
    painted before its key handler is attached rather than after.
  2. 0.148.0 additionally enters a repaint loop. 141 KB in 20 s against
    28 KB for the same run that got past the dialog: after the swallowed
    keystroke it redraws the full screen roughly every 80 ms indefinitely.
    0.149.0-alpha.1 no longer does this (9.7 KB) but still drops the key.
Expected

A keystroke delivered after the trust screen has rendered is acted on,
whatever the delay. A TUI that paints a prompt is contracting to accept the
answer to it.

Workarounds we found

All of these clear the dialog on 0.148.0; each needs a wall-clock delay, which
is why none of them is a real fix for a driver that only watches output:

  • ENTER ≥ 3 s after the marker
  • ENTER immediately and again 3 s later
  • 1 then ENTER, both ≥ 3 s after the marker
  • Down, Up, ENTER, all ≥ 3 s after the marker

Note that waiting for the terminal to go idle is not a usable substitute:
the trust screen animates, emitting a full repaint about every 80 ms in both
0.147.0 and 0.148.0, so an idle window never opens.

Environment
  • Linux 6.8.0 x86_64, TERM=xterm-256color, 80×30 pty
  • @openai/codex installed from npm with npm install --prefix
  • clean HOME per run, no ~/.codex state, no config.toml
  • 0.147.0 vs 0.148.0 vs 0.149.0-alpha.1, back to back on the same machine
Impact for us

We pin @openai/codex@0.147.0 in CI while this is open; the pin is what keeps
our Codex end-to-end leg meaningful, since on 0.148.0 it fails before any
request reaches the server under test.

Dominant language
Rust
Stars
125k
Forks
19.5k
Avg merge
1m
Merged PRs (30d)
1k

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from openai/codex

All issues in openai/codex

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.