Windows Desktop 26.707.72221: invalid heartbeat target `thread` is accepted and retried every 4–5s, causing high CPU
[!IMPORTANT] Root-cause correction (2026-07-15): Subsequent investigation showed that/index.htmlwas incidental, not the source of the literalthreadvalue. A malformed heartbeat automation was created withtargetThreadId: "thread", and the automation API accepted and persisted it astarget_thread_id = "thread". Desktop then retriedthread/readandthread/resumeevery approximately 4–5 seconds. Pausing the automation, correcting its target to a UUID, and restarting Desktop eliminated the retry loop and returned CPU to idle. See the root-cause and recovery comment. The original diagnostic narrative below is retained as historical evidence but its route-parsing conclusion is superseded.
Environment
- OS: Windows 10 Home 22H2, build 19045.6466, x64
- Codex/ChatGPT Desktop release reported in logs: 26.707.72221
- Microsoft Store package: OpenAI.Codex_26.707.9981.0_x64
- CPU: 16 logical processors
- RAM: 31.9 GB total, about 17.7 GB free during reproduction
- C: about 129.5 GB free
- Agent environment: Windows native / PowerShell
Summary
Immediately after a Windows reboot and Codex Desktop launch, the Electron browser-main process sustained roughly one logical core of CPU and showed large memory churn. The Desktop log shows that the root route /index.html was repeatedly interpreted as the literal conversation ID thread.
The app repeatedly issued thread/read and thread/resume for conversationId=thread. The app server correctly rejected those requests because thread is not a UUID. The retry loop continued for about 13 minutes 44 seconds, then stopped after navigation to a valid UUID thread.
Process evidence
- Hot process:
ChatGPT.exe, PID 8504, Electron rolebrowser-main codex.exeapp-server itself was not the hot process- 8-second sample: 90.0% of one logical core
- separate 15-second quiet sample: 88.1% of one logical core
- peak private/pagefile bytes: about 2.16 GB
- peak working set: about 2.18 GB
- after the retry loop ended, a 10-second sample was 0.2% of one logical core and private memory was about 245 MB
- cumulative CPU after 21.4 minutes: 794.2 CPU-seconds
Desktop log evidence
Log file:
%LOCALAPPDATA%\Codex\Logs\2026\07\15\codex-desktop-ecb701c5-af90-4a7d-9f78-12e96caf9609-8504-t0-i1-154108-0.log
Launch:
2026-07-15T15:41:08.438Z info Launching app ... platform=win32
Retry-loop signature:
maybe_resume_started conversationId=thread ... routePath=/index.html
Request failed conversationId=thread ... method=thread/read ...
invalid thread id: ... found `t` at 1
Failed to read thread metadata before resume conversationId=thread ...
Request failed conversationId=thread ... method=thread/resume ...
invalid session id: ... found `t` at 1
heartbeat_automation_resume_failed ... threadId=thread
Counts in this single launch:
maybe_resume_started conversationId=thread 179
Failed to read thread metadata before resume conversationId=thread 179
heartbeat_automation_resume_failed 179
method=thread/read 366
method=thread/resume 359
invalid-ID-related log lines 1259
Timeline:
first invalid-ID line: 2026-07-15T15:41:15.578Z
last invalid-ID retry: 2026-07-15T15:55:19.054Z
This is about one retry cycle every 4–5 seconds. The rate was stable at roughly 70–99 matching lines per minute.
Controls / confounders checked
- The persisted global state did not contain a key or value equal to
thread. heartbeat-thread-permissions-by-idcontained 108 keys, all valid UUIDs.thread/listresponses completed in 23 ms to 2.5 s; they did not remain pending for the duration of the high-CPU period.- Bundled plugin reconciliation completed after about 2.5 minutes, while the invalid-ID retry continued for about 14 minutes.
- System memory and disk free space were not exhausted.
- GPU and renderer child processes were not the sustained hot process.
The profile is large and may amplify the problem:
active rollout files: 1629
active rollout size: 5.18 GB
archived rollout files: 217
archived rollout size: about 0.57 GB
state_5.sqlite threads: 1836 total / 1620 active
active thread title+preview+first_user_message: about 5.99 million characters
logs_2.sqlite: about 618 MB
prompt history: 299 entries / about 1.39 MB
However, the persisted state contains no invalid thread ID, and the retry loop itself is directly visible in the Electron main log.
Minimal reproduction
- Reboot Windows.
- Launch Codex Desktop.
- Leave the app on the initial/root route before a valid thread UUID is selected.
- Observe the main
ChatGPT.exeprocess in Task Manager or sample its CPU. - Inspect the Desktop main log under
%LOCALAPPDATA%\Codex\Logs\.... - Observe repeated
conversationId=thread,thread/read,thread/resume, andheartbeat_automation_resume_failedmessages. - Navigate to a valid existing thread. In this reproduction, the invalid-ID loop stopped shortly afterward and CPU returned to idle.
Expected behavior
The root/no-active-thread route must not be interpreted as a thread ID. Desktop should validate a conversation ID as a UUID before calling thread/read, thread/resume, or heartbeat automation resume. A failed invalid-ID request should not schedule an unbounded retry.
Actual behavior
Desktop repeatedly treats the route token thread as an actual conversation/session ID and retries every few seconds, consuming sustained CPU and causing large memory churn for approximately 14 minutes.
Related issues
- #30021: Windows main process burns a CPU core while visible and idle
- #31531: Windows 10 high CPU/disk after update
- #20864: session-directory/history scalability
- #24510: unbounded active-thread metadata and history/list processing
- #33169: 26.707.72221 freezes when accessing current threads
I can provide the complete Desktop log and share the diagnostic Codex session privately through in-app feedback.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗