CLI/TUI stops reading stdin mid-session while PTY input backlog grows (0.130.0, multi-session)
Summary
I hit a Codex CLI/TUI freeze twice where one active session stopped responding to keyboard input entirely, but the underlying PTY continued receiving bytes. Killing the frozen codex process immediately returned control to the shell, which then displayed/consumed the buffered input.
I observed this on codex-cli 0.130.0. I have now upgraded to 0.132.0, but I have not yet confirmed whether the problem still occurs there.
This may be related to #20213, but I am not sure the root cause is the same.
Environment
- Variant: Codex CLI / TUI
- Observed version:
0.130.0 - Current installed version after upgrade:
0.132.0(not yet validated against this bug) - Platform: Linux x86_64
- Install method: global npm install
- Multiple concurrent
codexCLI instances sharing the default~/.codex
What I saw
One codex TUI session froze and stopped reacting to input. The shell underneath was not dead:
- typing keys into the frozen TUI produced no visible response
- after killing the frozen
codexprocess, the underlying shell immediately received/displayed the buffered keystrokes
This is the second time I have run into this issue.
Reproduction
I do not have a minimal deterministic repro yet, but the common factors were:
- Run multiple
codexCLI sessions concurrently. - Let them share the same default
$CODEX_HOMEstate/log databases. - After some time, one session can become permanently unresponsive to keyboard input.
- Killing that
codexprocess returns to the shell, and the previously typed input is still there.
Evidence collected from the frozen instance
I inspected the frozen process locally using strace, /proc, and PTY queue checks.
1. Input reached the PTY, but Codex stopped reading it
For the frozen session's PTY:
- unread input backlog was present and stable
- it stayed at
135bytes, then later141bytes - the backlog did not drain while the TUI was frozen
This strongly suggests terminal input reached the PTY but was no longer being consumed by the codex process.
2. Frozen process had stdin/stdout/stderr attached to the right PTY
For the frozen codex process:
fd 0 -> /dev/pts/0fd 1 -> /dev/pts/0fd 2 -> /dev/pts/0
3. During the freeze, there was no read(fd 0)
A short strace window on the frozen codex process showed:
- no
read(... </dev/pts/0> ...) - no TUI writes to that PTY during the same window
Meanwhile the unread PTY backlog remained nonzero.
4. Codex had an epoll instance watching stdin, but no thread was actually blocked on that epoll anymore
From /proc/<pid>/fdinfo, the frozen process had an epoll instance watching stdin:
- one
eventpollfd watchedtfd: 0(stdin / the PTY), plus one socket and one eventfd
But at freeze time:
- no thread in that process was blocked in
epoll_wait/epoll_pwaiton that stdin-watching epoll - no thread was calling
read(fd 0) - the extra
codexthread that looked like the stdin/input reactor was stuck infutex_wait_queue
For comparison, a healthy concurrent codex instance on the same machine still had a dedicated codex thread blocked in epoll_pwait(...) on its stdin-watching epoll.
That difference makes this look like the input reactor for the frozen TUI dropped out of its epoll loop.
Potential correlation with shared SQLite state/logs
I also noticed the shared local databases were fairly large at the time:
logs_2.sqlite:479166464byteslogs_2.sqlite-wal:102176032byteslogs_2.sqlite-shm:229376bytes
Because this happened with multiple concurrent Codex sessions sharing the same $CODEX_HOME, this might be related to the SQLite contention/freeze class described in #20213.
However, I do not have proof that SQLite contention is the direct cause of this particular freeze. What I can say confidently is:
- input reached the PTY
- the frozen
codexprocess stopped reading stdin - its stdin-watching epoll/reactor path appeared to be inactive
Additional notes
- This was not a browser or terminal transport issue on my side.
- After killing
codex, the shell immediately behaved normally and consumed the buffered input. - I will watch whether this still reproduces on
0.132.0, but I wanted to file the evidence from the0.130.0failure now in case it matches an existing known bug.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗