Windows TUI livelock: main thread spins 100% user-mode in a parser/scan loop — UI frozen, zero console writes, session otherwise keeps working (0.145.0)

Resolved 💬 2 comments Opened Jul 22, 2026 by zackees Closed Jul 22, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

An interactive codex TUI session on Windows wedged into a livelock: one thread (the TUI/main thread) pins a full core in a pure user-mode spin while the process writes zero bytes to the console — the UI is completely frozen — yet the agent session itself keeps working (network streams stay active on other threads). The spin is bursty after an initial sustained phase: ~100% of a core for the first ~50 minutes of the process, then intermittent, and it was re-observed at 104% of a core ~2.8 h in while diagnostics below were captured live. The UI never recovers.

I captured instruction-pointer samples, a stack snapshot, and instruction bytes from the live wedged process so the spin site can be symbolicated against your build.

Environment

  • codex: 0.145.0 (@openai/codex npm, vendored native binary codex-win32-x64/vendor/x86_64-pc-windows-msvc/bin/codex.exe, 359,245,096 bytes, installed 2026-07-21)
  • OS: Windows 10 Pro 10.0.19045, Windows Terminal 1.24.11321.0
  • CPU: AMD Ryzen 7 3700X (8C/16T)
  • Launch: interactive TUI (no -p/exec), inherited console, started via cmd /D /S /C "chcp 65001 > nul & codex.cmd -c plugins..." from a wrapper CLI inside a Windows Terminal tab
  • Load context: 8 concurrent codex TUI sessions plus heavy parallel rustc builds — the machine was at 100% CPU around wedge onset. Only this one session wedged; the other 7 stayed healthy.

Timeline & process measurements (live, same wedged process)

Process start 09:05:11. All numbers from live sampling of pid 43140:

| time | uptime | thread CPU (spin thread) | note |
|---|---|---|---|
| ~09:53 | ~48 min | 46.5 min, state=Running | ≈97% duty since launch — spin effectively began at/near startup |
| 11:52 | 167.7 min | 52.7 min | only +6.2 min over the interval → ~5% duty (bursty phase) |
| 11:53 | — | instantaneous 104% of one core | spinning again during IP sampling |

  • 37 threads total; all CPU is one thread (the rest sit in Wait). Thread started with the process.
  • CPU split: 47.2 min user / 1.4 min kernel (97% user-mode) → not a hammered failing syscall; a pure in-process loop.
  • IO read = 0 B/s, IO write = 0 B/s while spinning — the TUI presents nothing (frozen frame); the tab's OpenConsole host is idle.
  • TCP: 2 Established connections alive (+1 CloseWait) → the agent side keeps functioning. Responses presumably accumulate invisibly.

Where it spins — IP sampling of the hot thread

30 samples at 100 ms intervals via SuspendThread/GetThreadContext/ResumeThread. Module base for this run: codex.exe @ 0x7ff65a8c0000 (offsets below are RVAs, stable for the 0.145.0 win32-x64 binary). All 30 samples land inside codex.exe — zero in ntdll/kernel32 (the loop makes no blocking calls):

 6x  codex.exe+0xf5ae8d4      3x  codex.exe+0xf5ae907      3x  codex.exe+0xf5ae8fc
 5x  codex.exe+0xf5aa349      2x  codex.exe+0xf5ae8f0      2x  codex.exe+0xf5aace9
 2x  codex.exe+0xf59b82d      1x each: +0xf5aac7e +0xf5acb8e +0xf59b820 +0xf5acb9a +0xf5ae8e9 +0xf5ae8d1 +0xf5aacdc

Everything clusters in one ~76 KB region (+0xf59b8xx+0xf5aexxx). Return-address scan of the thread stack (top frames) points into the same neighborhood, with older frames elsewhere:

codex.exe+0xf5a8d06, +0xf5a968e, +0xf5a64da   <- live call chain around the spin region
codex.exe+0xd9cedc5, +0xf8b7359, +0xf8a0ac4, +0xd9cb658, +0xd9d3a12, +0xc5b8187, +0xd90d657  <- older frames

Instruction bytes at the hot sites (for symbolication)

codex.exe+0xf5ae8c0 (80 bytes; contains the hottest RIP +0xf5ae8d4):

00 3B C8 72 08 85 C0 0F 85 ED 01 00 00 48 8B 75 08 41 8B 06 83 F8 53 0F 87 1A 02 00 00 48 8B 7D 28 48 8B 75 10 4C 8B 5D 08 4C 8B 95 68 02 00 00 4C 8B 45 50 48 8B 55 18 44 8B 4D 4C 48 8B 5D 38 4C 8B BD D8 00 00 00 E9 2D BA FF FF 48 C7 47 18

codex.exe+0xf5aa330 (contains +0xf5aa349):

00 FF FF FF FF 48 89 5D 38 48 98 48 8D 0D BE 5C A5 F0 8B 8C 81 1C EB 5A 0F 48 8D 05 B0 5C A5 F0 48 03 C8 FF E1 83 BD D4 00 00 00 00 74 06 4C 3B 6D 30 75 25 4C 8B B5 60 02 00 00 41 8B F5 41 2B F6 89 75 58 75 20 83 BD D0 00 00 00 00 74 17 C7

codex.exe+0xf59b810 (contains +0xf59b820/+0xf59b82d):

C4 40 41 5F 41 5E 41 5C 5F 5D C3 CC CC CC CC CC 0F B6 01 48 8D 0D 16 F0 05 02 8B 04 81 C3 CC CC 4C 8B C2 48 3B CA 73 45 4C 8D 15 01 F0 05 02 90 0F B6 11 0F B6 C2 24 C0 3C 80 74 37 45 8B 0C 92 48 FF C1 41 83 F9 01 7E 1F BA 01 00 00 00 66 90

codex.exe+0xf5aacc0 (contains +0xf5aacdc/+0xf5aace9):

8B 4E 08 8B D0 E8 26 20 FF FF 85 C0 0F 84 BB FA FF FF E9 C8 F9 FF FF 41 0F B6 4D 00 49 8B 46 10 8B D1 48 C1 EA 05 8B 04 90 0F A3 C8 72 59 49 FF C5 E9 8C FA FF FF 49 2B DD 48 83 FB 01 0F 8C 86 F6 FF FF 49 8B CD 41 FF 17 48 63 F8 48 3B DF 7C

Reading the bytes: the +0xf5aa330 site is a computed jump through a table (lea rcx,[rip+…]; mov ecx,[rcx+rax*4]; lea rax,[rip+…]; add rcx,rax; jmp rcx) guarded upstream by cmp eax,0x53; ja … at +0xf5ae8c0 — i.e. an ~84-way jump-table state machine. The +0xf59b810 site is a byte-class table lookup loop (movzx eax,[rcx]; mov eax,[table+rax*4] and a and al,0xC0; cmp al,0x80 UTF-8 continuation-byte test). Together this looks like a text/VT/markdown parser or width-scanning loop that under some condition either never terminates or reprocesses an ever-growing buffer on every pass — while the render side never presents a frame (0 console writes).

Reproduction

Not deterministic, unfortunately. Conditions when it happened: long-lived interactive session doing agent work in a Rust repo, machine under sustained 100% CPU from parallel builds, 7 sibling codex sessions healthy. The wedge began at/near process start and the UI has been frozen since; input (keystrokes) has no visible effect, but the underlying session demonstrably continues (active TCP streams, tool activity observed on the repo).

Notes

  • Happy to run further live diagnostics against the wedged process (it is still running as I file this) — e.g. targeted memory reads, more IP samples, or a private minidump (I'd rather not attach a dump publicly since the heap contains the conversation).
  • Wrapper-side tracking issue with the detection/mitigation plan: zackees/clud#541

View original on GitHub ↗

2 Comments

github-actions[bot] contributor · 1 month ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #33755

Powered by Codex Action

zackees · 1 month ago

Confirmed duplicate of #33755 — same signature from the other side: my IP sampling shows an ~84-way jump-table dispatch loop + UTF-8 byte-class scanning (i.e. a regex/parser opcode interpreter), which is exactly syntect/Oniguruma highlighting; the reporter's macOS stack sample names those frames directly. Adds a Windows 10 / WT datapoint for the same bug.

Version check: the fix (#34796, merge 5381edb, 2026-07-22 17:39 UTC) is NOT in 0.145.0 (built 2026-07-21 18:21 UTC) nor in 0.146.0-alpha.2 (built 2026-07-22 07:22 UTC) — it currently exists only on main and should ship in the next cut. Will re-test when that release lands and reopen if the Windows wedge persists (noting the fix caps highlighting at 4 KiB per line; if a pathological input just under that threshold can still stall a frame for minutes, a time-budget guard might be worth considering too).

Closing as duplicate.