codex - 💡(How to fix) Fix Desktop thrashes resume/unsubscribe when >4 heartbeat automations have target_thread_id [5 comments, 3 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openai/codex#19563Fetched 2026-04-26 05:14:56
View on GitHub
Comments
5
Participants
3
Timeline
14
Reactions
0
Author
Timeline (top)
commented ×5labeled ×3mentioned ×2subscribed ×2

Fix Action

Fix / Workaround

Additional real-world mitigation evidence

Code Example

thread/read -> thread/resume -> maybe_resume_success -> thread/unsubscribe

---

maybe_resume_success ... latestTurnStatus=completed markedStreaming=true

---

Codex main CPU: up to ~190-224%
Renderer CPU: up to ~63-108%
App server CPU: up to ~36-69%
phys_footprint during loop: ~4.9 GB
phys_footprint_peak: 9.2 GB

---

thread/read: 1,424
thread/resume: 1,423
thread/unsubscribe: 1,423
maybe_resume_success: 1,423

---

Five active cron automations with no linked target thread:
- no CPU/Energy explosion
- no sustained resume/unsubscribe loop

---

more than 4 heartbeat automations with target_thread_id

---

pinned threads
large thread histories
stale plugins
malformed old automation state
actual automation execution
no-thread cron automations
runaway model calls

---

4 PAUSED heartbeat automations with target_thread_id
1 ACTIVE heartbeat automation with target_thread_id

---

Codex main CPU: ~224% -> ~0.9%
app-server CPU: ~25% -> ~0.1%
renderer CPU: ~28% -> ~0.0%
codex_chronicle: ~2.5%

---

thread/resume
maybe_resume_success
inactive_thread_unsubscribed

---

resume completed target -> mark streaming/owner -> exceed inactive owner cap -> unsubscribe -> needs_resume -> resume again
RAW_BUFFERClick to expand / collapse

What version of the Codex App are you using?

Codex Desktop 26.422.30944 (CFBundleVersion 2080)

Bundled CLI observed during the investigation: codex-cli 0.125.0-alpha.3

What platform is your computer?

macOS 26.0.1 (25A362), Apple Silicon / arm64

What issue are you seeing?

Codex Desktop can enter a persistent local CPU/Energy loop when more than four heartbeat automations have a linked target_thread_id.

The Desktop repeatedly cycles completed heartbeat target threads through:

thread/read -> thread/resume -> maybe_resume_success -> thread/unsubscribe

The suspicious detail is that the turns are already completed, but the resume path still marks the conversation streaming:

maybe_resume_success ... latestTurnStatus=completed markedStreaming=true

The cleaner finding is that this does not require giant histories, pinned threads, stale Chronicle state, memory backfill, or actual automation execution. It reproduces with fresh tiny target threads when the fifth linked heartbeat automation is enabled.

In the original real-world case, large histories made the impact much worse:

Codex main CPU: up to ~190-224%
Renderer CPU: up to ~63-108%
App server CPU: up to ~36-69%
phys_footprint during loop: ~4.9 GB
phys_footprint_peak: 9.2 GB

What steps can reproduce the bug?

A clean repro from the same machine:

  1. Create five heartbeat automations.
  2. Give each one a target_thread_id pointing to a target thread.
  3. The target threads can be fresh and tiny; each only had one trivial turn like user say ok, assistant ok.
  4. Enable the fifth linked heartbeat automation.
  5. Leave Desktop idle and watch ~/Library/Logs/com.openai.codex/... plus Activity Monitor / process CPU.

When the fifth linked heartbeat was enabled, Desktop entered the resume/unsubscribe loop across all five fresh target threads.

Approximate one-minute log counts from the clean repro:

thread/read: 1,424
thread/resume: 1,423
thread/unsubscribe: 1,423
maybe_resume_success: 1,423

The loop was evenly distributed across the five heartbeat target threads. Each showed latestTurnStatus=completed markedStreaming=true and turnCount=1.

A negative-control case did not reproduce:

Five active cron automations with no linked target thread:
- no CPU/Energy explosion
- no sustained resume/unsubscribe loop

So the narrowed trigger looks like:

more than 4 heartbeat automations with target_thread_id

It does not appear to be:

pinned threads
large thread histories
stale plugins
malformed old automation state
actual automation execution
no-thread cron automations
runaway model calls

automation_runs remained 0 during the loop, and token-count events appeared only for actual thread/heartbeat turns, not for each resume/unsubscribe cycle. This looks like a local Desktop subscription/resume loop rather than runaway token usage.

Additional real-world mitigation evidence

In the original affected Desktop state, the loop involved five heartbeat automation target threads:

4 PAUSED heartbeat automations with target_thread_id
1 ACTIVE heartbeat automation with target_thread_id

That means paused automation target links were still enough to keep completed target threads in the UI/subscription/resume set.

I commented out only the target_thread_id lines for the four PAUSED heartbeat automations, leaving the automation files present and still paused. The active heartbeat was left unchanged.

Immediately after detaching those paused target threads:

Codex main CPU: ~224% -> ~0.9%
app-server CPU: ~25% -> ~0.1%
renderer CPU: ~28% -> ~0.0%
codex_chronicle: ~2.5%

A fresh 8-second log window after the detach had no new entries for:

thread/resume
maybe_resume_success
inactive_thread_unsubscribed

This suggests the trigger is the Desktop handling of linked heartbeat target threads, not the heartbeat jobs actually firing.

Code-path clue from the packaged app

From the local packaged app (/Applications/Codex.app/Contents/Resources/app.asar), the behavior lines up with a subscription state-machine loop:

  • The inactive owner stream cap is 4 (maxInactiveOwnerThreads).
  • The resume success path calls the equivalent of markConversationStreaming(...) and setConversationStreamRole(... { role: "owner" }) even when the latest turn is already completed.
  • The inactive cleanup path then unsubscribes completed inactive owner threads and sets them back to a needs_resume state.

That appears to create this cycle once there are five linked heartbeat targets:

resume completed target -> mark streaming/owner -> exceed inactive owner cap -> unsubscribe -> needs_resume -> resume again

What is the expected behavior?

  • Completed heartbeat target threads should not be repeatedly resumed and marked streaming while Desktop is idle.
  • Paused heartbeat automations should not keep their target threads in a hot resume/subscription loop.
  • If Desktop needs target-thread metadata for the automations UI, it should use a cheap metadata path rather than repeatedly hydrating/resuming completed conversations.
  • More than four linked heartbeat automations should not cause sustained multi-core CPU usage or multi-GB memory footprint spikes.

Additional information

This is separate from #19516. That issue is about an orphaned codex_chronicle process surviving a killed Desktop app and holding the Chronicle lock. The bug reported here reproduces without an orphaned Chronicle process, with Chronicle and memory disabled, and with fresh tiny heartbeat target threads.

Related issues:

  • #19516: separate orphaned Chronicle lock-wait loop after killing Desktop.
  • #10952: includes a similar maybe_resume_success ... latestTurnStatus=completed markedStreaming=true symptom, but appears framed around one desynced thread after request_user_input in Default mode.
  • #16271: crash/resume loop around interrupted threads and missing rollout files on Windows.

Possible fix directions:

  • Do not call markConversationStreaming in the resume path when the latest turn status is already completed.
  • Add a guard/backoff around repeated thread/resume for completed idle target threads.
  • Keep paused heartbeat target threads out of the hot subscription/resume set.
  • Treat inactive unsubscribe state transitions carefully so resumeState=needs_resume does not immediately re-trigger hydration for non-active completed target threads.
  • Add a cheap metadata path for automation target-thread lists that does not require full conversation snapshots.

extent analysis

TL;DR

The issue can be fixed by modifying the Codex Desktop application to prevent repeated resume and unsubscribe cycles for completed heartbeat target threads.

Guidance

  • Review the subscription state-machine loop in the Codex Desktop application to identify the root cause of the cycle.
  • Consider adding a guard or backoff mechanism to prevent repeated thread/resume calls for completed idle target threads.
  • Investigate the possibility of keeping paused heartbeat target threads out of the hot subscription/resume set to prevent unnecessary resume and unsubscribe cycles.
  • Examine the markConversationStreaming function to determine if it should be called when the latest turn status is already completed.

Example

No code snippet is provided as the issue does not include specific code that needs to be modified. However, the markConversationStreaming function and the subscription state-machine loop are potential areas to investigate.

Notes

The issue appears to be specific to the Codex Desktop application and its handling of heartbeat target threads. The provided information suggests that the issue is not related to other components or systems.

Recommendation

Apply a workaround by commenting out the target_thread_id lines for paused heartbeat automations, as described in the issue, to prevent the resume and unsubscribe cycles. This workaround can help mitigate the issue until a permanent fix is implemented.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

codex - 💡(How to fix) Fix Desktop thrashes resume/unsubscribe when >4 heartbeat automations have target_thread_id [5 comments, 3 participants]