Windows Desktop 26.707.72221: invalid heartbeat target `thread` is accepted and retried every 4–5s, causing high CPU

Open 💬 1 comment Opened Jul 15, 2026 by Pluglug
[!IMPORTANT] Root-cause correction (2026-07-15): Subsequent investigation showed that /index.html was incidental, not the source of the literal thread value. A malformed heartbeat automation was created with targetThreadId: "thread", and the automation API accepted and persisted it as target_thread_id = "thread". Desktop then retried thread/read and thread/resume every 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 role browser-main
  • codex.exe app-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-id contained 108 keys, all valid UUIDs.
  • thread/list responses 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

  1. Reboot Windows.
  2. Launch Codex Desktop.
  3. Leave the app on the initial/root route before a valid thread UUID is selected.
  4. Observe the main ChatGPT.exe process in Task Manager or sample its CPU.
  5. Inspect the Desktop main log under %LOCALAPPDATA%\Codex\Logs\....
  6. Observe repeated conversationId=thread, thread/read, thread/resume, and heartbeat_automation_resume_failed messages.
  7. 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.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗