Codex Desktop high CPU from unbounded active thread metadata and local history/list processing
Codex Desktop app-server high CPU from unbounded active thread metadata / thread-list path
Summary
Codex Desktop can sustain high CPU/GPU usage when the local profile contains a
large number of active threads with large title / preview /first_user_message metadata in ~/.codex/state_5.sqlite.
This appears to be a thread-list / sidebar / local-history scalability issue.
The usage pattern is high-volume, but it is within expected Codex workflows:
long local sessions, repeated agent/review/automation runs, and many project
threads.
The hottest path we observed was not model inference. It was local state
enumeration and metadata handling around active thread listing.
Environment
- Codex Desktop:
26.519.41501 - Platform: macOS
26.4.1 - Hardware: Apple Silicon
- Local state DB:
~/.codex/state_5.sqlite - Local sessions:
~/.codex/sessions/.../rollout-*.jsonl
Observed Symptoms
- Codex Desktop /
codex app-serverkeeps using CPU even when no intentional
user task is running.
- Desktop renderer/GPU can also become warm depending on where the bottleneck
surfaces.
- Restarting Codex may temporarily change the active PID, but the expensive
local-state path returns.
- The problem correlates with large active local thread metadata and repeated
thread-list/sidebar/reconnect activity, not with a single running model call.
Local Diagnostics
Current redacted profile statistics after one manual cleanup pass:
active threads: 9,456
active metadata payload chars: 317,838,640
archived threads: 3,264
archived metadata payload chars: 328,436,582
Before that cleanup, the profile had roughly:
active threads: 12,712
active metadata payload chars: ~403 MiB
one repeated review/agent group: 3,256 rows, ~201 MiB
The slow active thread-list query shape observed locally:
SELECT ...
FROM threads
WHERE archived = 0
AND preview <> ''
AND model_provider IN (?)
AND created_at_ms < ?
ORDER BY created_at_ms DESC
LIMIT ?
Query plan:
SEARCH threads USING INDEX idx_threads_archived (archived=?)
USE TEMP B-TREE FOR ORDER BY
So the query starts from archived=0, scans a very large active set, and then
sorts for the sidebar/list path. The cost is amplified because selected columns
include large text metadata used as title/preview/first-user-message.
Long-running Goal Session Evidence
One long-running goal session also showed that active local sessions can keep
the history/logging path warm for a long time.
Redacted local session:
thread id: <redacted local thread id>
created: 2026-05-25 22:26:26 KST
still active in state DB at inspection time
cwd: /Users/.../<project>
rollout JSONL size: ~38.7 MB and still growing
state DB tokens_used: 511,046,356
state DB has_user_event: 0
Rollout JSONL event counts at inspection time:
lines: 22,546
function_call: 4,879
function_call_output: 4,875
thread_goal_updated: 3,998
token_count: 3,459
reasoning: 1,894
message: 1,061
agent_message: 1,002
custom_tool_call: 388
custom_tool_call_output: 388
patch_apply_end: 374
context_compacted: 28
Tool-call distribution:
exec_command: 3,408
write_stdin: 1,271
update_plan: 196
get_goal: 3
create_goal: 1
The thread_goal_updated events were emitted very frequently:
thread_goal_updated count: 3,998
median interval: 6.148 seconds
p95 interval: 33.085 seconds
p99 interval: 54.032 seconds
largest observed gap: 5,015.697 seconds
Near the end of the sample, the rollout was still appending events such as:
write_stdin call
thread_goal_updated status=active tokens=13,127,963 seconds=34,696
function_call_output
token_count
So this was not just a large historical file. The active goal session was still
emitting progress/heartbeat-like events and polling a running command.
logs_2.sqlite also showed a short retained window for this same thread:
time window: 2026-05-26 09:24:47-09:27:22 KST
rows: 923
estimated bytes: 10,259,493
largest row: 781,177 bytes
top target: codex_client::transport|TRACE
top target bytes: 9,016,481 across 12 rows
process: codex resume <same thread id>
In a 15 second observation window, the rollout file continued to grow by 2,627
bytes. The log table appeared to be subject to retention/pruning, so row counts
can decrease while the max timestamp continues advancing.
Important confounder: this specific session also still had a real child task
running under the Codex resume process:
codex resume <same redacted thread id>
child: python scripts/<project-audit>.py --skip-fetch --force
child CPU at inspection: ~100%
cwd: /Users/.../<project>
That means this particular long-goal sample should not be described as an
entirely idle Codex session. It does show, however, that very long active goals
can keep Codex's rollout/log/session-sync surfaces hot, and it can be hard for a
user to tell from the Desktop UI which old goal/session is still driving local
work.
Why This Looks Like A Product Scalability Bug
The trigger is heavy local usage, but the usage itself is not exotic for Codex:
- users can create thousands of threads
- some threads can be very long
- agent/review/automation flows can create many repeated sessions
- long-running goals can stay active for many hours and continue writing local
progress/log events
- sidebar, resume, reconnect, and archive reconciliation should have bounded
cost regardless of total local history size
The app appears to treat local conversation history more like directly coupled
UI metadata than like a large local product database. The list/sidebar path
should probably operate on bounded metadata projections and lazy-load full
history only when a specific thread is opened.
Related Issues
These seem adjacent or overlapping:
- #21007 macOS Desktop UI lag with excessive codex disk writes despite low system resource pressure
- #20435 Codex Desktop renderer spins at high CPU while codex MCP servers are idle
- #12709 Codex Desktop on macOS becomes progressively slow and causes system-wide lag during long MCP-heavy sessions
- #12644 Codex desktop app generates excessive file I/O, causing severe CPU load
- #22283 Codex App becomes choppy when session history contains a large user message
- #21211 Thread navigation/loading slows from unbounded metadata and eager large-history hydration
- #22411 app-server loads ALL session files on every thread/list call
- #18693 Desktop performance collapses in profiles with a few very large local conversation histories
- #22053 Codex Desktop main Electron process pegs CPU while app-server is idle
- #23849 Codex Desktop on macOS loses message hover after long session; main/renderer/GPU stay hot
- #23851 Desktop:
thread.archivedcan be reset by reconciliation after direct DB archive
This report may be the same family as #21211 / #22411, but the local evidence
here points specifically at active state_5.sqlite metadata and query/index
shape.
Workaround / Mitigation Script
I also prepared a local mitigation script that may help other affected users
diagnose and temporarily reduce the active-list load.
It does not patch Codex itself. It is a user-side workaround.
Script gist:
https://gist.github.com/kangminlee-maker/f933aade364a02b176d899e5c8a2486d
The script:
- reports active thread metadata size from
~/.codex/state_5.sqlite - detects repeated high-volume active thread groups dynamically
- keeps one real active representative thread per group
- archives older sibling threads
- writes
archive_groups.jsonlso archived siblings can be searched/restored - can create optional helper SQLite indexes for the observed thread-list query
- can copy/move old archived rollout JSONL files to cold storage
- defaults to dry-run and backs up
state_5.sqlitebefore applying changes
Safety notes:
- Do not use this for SQLite corruption or migration checksum mismatch issues.
- Run the report/dry-run first.
- Close Codex before applying changes.
- Keep the generated backup.
- Direct SQLite edits are unsupported and may interact with Codex reconciliation.
Example dry-run:
python3 scripts/codex_state_batch.py batch --policy config/sample-policy.json
Example apply after closing Codex:
python3 scripts/codex_state_batch.py batch --policy config/sample-policy.json --apply
Dry-run result on my affected profile:
candidate archive threads: 1,000
candidate metadata payload chars: 140,384,001
dynamic groups detected: 10
representative manifest entries planned: 5
cold archive candidates: 2,447 rollout JSONL files, 813,736,111 bytes
The mitigation is attached as a small script package. It is meant as diagnostic
evidence and a temporary local workaround, not as an official fix.
Expected Behavior
Codex Desktop should keep thread-list/sidebar/reconnect paths bounded:
- list views should read small metadata projections only
- very large
title/preview/first_user_messagefields should be capped
or summarized
- thread history should be hydrated lazily
- active sidebar queries should use indexes that satisfy both filtering and
ordering
- archive state should remain stable across reconciliation
- heavy local profiles should not keep app-server / renderer / GPU hot while
idle
Actual Behavior
Large active local metadata causes expensive repeated local processing. Depending
on the profile and app state, the visible bottleneck can appear in SQLite,
app-server CPU, Electron main/IPC, renderer, or GPU, but the common source is the
unbounded local-history/list path.
26 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Thanks for the duplicate suggestions. I reviewed #24427, #23919, and #23072.
I agree they are related to the same broader Desktop performance family, especially around many local sessions, long histories, and high CPU. I am going to leave this issue open for now because the distinguishing evidence here is the local state/list path:
state_5.sqliteactive metadata size measurementslogs_2.sqliteTRACE/log growth evidencethread_goal_updatedeventsIf maintainers prefer one canonical tracking issue, I am happy for this to be closed as a duplicate, but I wanted to preserve the state DB/query-plan evidence because it may help isolate the product-side fix.
Codex #24510: Desktop High CPU from Unbounded Thread Metadata
Issue Summary
Codex Desktop sustains high CPU/GPU usage when the local profile contains a large number of active threads with large metadata in
~/.codex/state_5.sqlite. The hottest path is local state enumeration and metadata handling around active thread listing.Profile stats: 9,456 active threads, 317M chars of metadata payload. Before cleanup: 12,712 threads, ~403 MiB metadata.
Root Cause Analysis
The thread-list query loads full metadata (title, preview, first_user_message) for ALL active threads into memory, then processes them in the UI thread. With 10K+ threads and 400MiB of metadata, this causes:
Proposed Fix
Fix 1: Lazy Metadata Loading (Critical)
Fix 2: Metadata Size Limits
Fix 3: Auto-Cleanup Old Threads
Impact
Thanks for the summary and proposed fixes. I agree with the general direction: bounded list metadata, lazy loading, and limiting stored preview/title size would likely help.
One small clarification so the evidence stays precise:
state_5.sqlitequery shape, its query plan, and the large active metadata payload that the list path has to scan/sort.title,preview, andfirst_user_message; I did not see athread_namecolumn in this localthreadstable.So I would phrase the product fix as: keep the thread list bounded by using small indexed projections, cap or summarize large metadata fields, lazy-load full history/large metadata only when opening a specific thread, and make any cleanup/archival behavior explicit and reversible.
Only by setting additional indices, I got fewer freezes:
Make a backup
Add indices
Thanks for digging into this. I think this issue is very relevant to the slow
thread/listbehavior I am seeing in a related Codex Desktop freeze report.I checked the affected remote Codex profile, and it lines up closely with the query-shape concern described here:
~/.codex/state_5.sqlite: ~26 MB plus WAL2,4362,4288title,preview, andfirst_user_message:19,859,906characters~/.codex/sessions: ~1.5 GB~/.codex/logs_2.sqlite: ~605 MBThe remote
thread/listquery plan also matches the inefficient shape called out in this issue:That makes me think this issue likely explains a substantial share of the 10-25 second
thread/listcalls captured in the freeze investigation.There may still be a distinct Desktop/remote-SSH failure mode layered on top of it, though. In the related captures, the user-visible freeze tends to appear around remote SSH app-server reconnect/recovery. The logs show
app_server_connection.closed code=1006, app-server proxy termination, reconnect/recovery, and then clustered longthread/list/thread/resumecalls across multiple large remote conversations.So my current read is: this issue may be the underlying scalability problem, while the remote SSH reconnect path can amplify it into a visible Codex Desktop freeze.
Update after the latest Codex Desktop update:
I updated/restarted Codex Desktop and rechecked this issue on:
26.519.81530/ build3178rust-v0.134.0My read is that this release contains adjacent changes, but not a direct fix for the bottleneck described here. The changelog includes work around local conversation history search, thread content search, tracing/analytics, websocket/session behavior, and compaction. I did not find an explicit change for the active thread-list /
state_5.sqlitemetadata scan/sort path.The issue still appears reproducible in my local profile after restart:
state_5.sqlitecurrently has8,657active threadstitle+preview+first_user_messagepayload is still about202,756,007characters4,291threads and about468,871,981charactersupdated_atactive-list query path still uses a temp B-tree sort in my DB:One caveat: my local DB still has helper indexes from the temporary mitigation, so any improvement on the
created_atpath should not be treated as evidence that the product fix landed. Theupdated_atpath above is why I think this issue remains valid after the update.A note on the additional-index workaround from @35ujq435jq45: I think this is a useful tactical mitigation and it matches what I saw locally.
Composite indexes that include
archivedplus the list sort key can reduce or avoid the expensive active-list scan/sort path, especially when the query can satisfyORDER BY ... DESC, id DESCfrom the index. In my local mitigation, helper indexes around the active preview/list path made thecreated_atquery much faster.I would still treat this as a workaround rather than the full product fix:
model_provider,preview <> '', workspace/cwd filters, and whether the list is ordered bycreated_at_msorupdated_at_msUSE TEMP B-TREE FOR ORDER BYtitle,preview, andfirst_user_messagefields can still move a lot of dataSo I think the index workaround is valuable evidence and a good short-term mitigation for affected users, but the underlying scalability issue should still be fixed in the app/server query and metadata model.
This is an unusually high-quality scalability report 👀
What makes this especially interesting is that the bottleneck no longer appears to be model inference itself, but the runtime’s local session/state management surfaces.
The active-thread metadata explosion here is kind of a warning sign for long-running agent ecosystems:
conversation history slowly stops behaving like “chat”
and starts behaving like an operational database.
Once agents generate thousands of tool calls, rollouts, heartbeat events, summaries, previews, sidebar projections, reconnect state, etc., the runtime begins paying compounding costs just to maintain “awareness” of its own sessions.
A few things here feel especially important:
This starts looking less like a frontend issue and more like runtime memory topology / execution-state architecture.
We’ve been seeing similar patterns while exploring capability-routing and execution optimization layers:
event growth is often manageable,
but “session awareness surfaces” become the hidden scalability killer 😊
Really valuable investigation.
One design thought after the discussion above:
Keep the full history, but do not make the session list carry its weight. Add a buffer between history and UI, and split "active" into more than one state.
The current behavior seems to make
archived=0do too much work. "Not archived" can mean:Those are very different states, but they all appear to end up in the same active list surface.
I think a durable fix would be to keep the current local history/resume behavior, but put a small bounded list layer in front of it:
Indexes are a useful workaround for the current query shape, but they do not fully solve the design issue. They make the heavy path faster; they do not make the path lighter.
The main point is: the sidebar should show a bounded view of the local history, not become the place where the growing local history is repeatedly paid for.
I am seeing a closely related Codex Desktop failure on macOS after a Desktop update/restart. My case overlaps this issue, but adds a stale pinned-thread / missing-rollout path angle.
Environment:
~/.codex~/.codex/state_5.sqlite~/.codex/sessions/.../rollout-*.jsonlUser-visible symptoms:
Find SDP catalog filemade Codex Desktop difficult or impossible to click.Agent 2.1 : upper, also became unsafe to reopen from the sidebar.Local evidence:
state db returned stale rollout pathstale_db_pathfailed to resolve rollout path ... No such file or directory (os error 2)thread/read,thread/resume, andthread/goal/getstyle calls around a stale or non-selected thread.pinned-thread-idsuntil related Electron persisted atom/cache entries were cleaned.Workaround required locally:
pinned-thread-ids.Why I think this is in the same family as #24510:
Expected behavior:
Related issue: #20269.
I am seeing a related macOS Desktop failure shape on a current/newer build where the issue is not limited to one oversized rollout file.
Environment
26.602.30954/ build35750.137.026.5.1/ build25F80Impact
Important local finding
Two specific threads are risky to continue from Desktop:
The second thread is important because it means the trigger is not only Computer Use. That thread did use CodeGraph and Playwright/Next validation, but its stored rollout is only about
1.1 MB, not hundreds of MB. It is currently reported asnotLoadedby the Codex thread listing tool, with recent turns ininterruptedstate.The BI thread's rollout is also not huge: about
6.8 MB. Continuing it is still unsafe in practice.So the local evidence points to a combined failure shape:
codex doctor --all --asciievidenceThis is a heavy but realistic Codex Desktop usage profile: many sessions, many subagent-spawned threads, long-running interrupted work, and lots of persisted local history.
Current mitigation attempted
To keep the app usable, I switched the local config into an emergency low-memory mode:
notify = []to avoidCodex Computer Useturn-ended helper launch.After config parsing,
codex doctorreports:Existing stale helper processes still remain until a full app/helper cleanup, but new sessions should no longer eagerly initialize the large local MCP surface.
Request / expected behavior
Please treat this as a session/history lifecycle problem in addition to any individual MCP leak:
notLoadedorinterruptedthread should not synchronously hydrate enough local state to push the machine into memory pressure.Adding a newer-build macOS data point. This looks related to the local-history / thread-list / helper-process pressure described here, though my local profile is much smaller than the original report.
Environment:
User-visible symptoms:
Add to chat/Ask in side chatcontextual action.Representative process snapshot while Codex was still open and no intentional heavy project process was the focus:
Local state size at the same time:
A recent macOS DiagnosticReports Codex crash report exists locally from the same day, but I am not pasting the raw
.ipscontents publicly because it may contain local paths or session-specific details.My read is that this is the same broad Desktop scalability/lifecycle family, but in a profile with only hundreds of active threads rather than thousands. The combination of multiple windows/threads, local logs/history, and helper/MCP process trees still appears enough to keep app-server/main/renderer hot and make Desktop unstable.
Cross-machine benchmark data point from 2026-06-29 JST. This is metadata-only: no prompt text, log bodies, session JSONL line bodies, auth DBs, tokens, cookies, or secrets were exported.
I benchmarked Codex Desktop local load on four profiles:
~/.codex/sessions: 696 JSONL files, 1261.90 MiB totallogs_2.sqlite: 82.64 MiB + 5.58 MiB WAL, 24,250 rowssqlite3CLIlogs_2.sqlite: 96.49 MiB + 4.10 MiB WALlogs_2.sqlite76.73 MiB + 4.09 MiB WAL, 67,759 rows; count query 0.67 ms, top-thread fetch 4.08 ms, visible thread list 13.68 msInterpretation:
Optimization plan that would likely address this class of issue:
logs_2.sqlite/ WAL and add proactive checkpoint/compaction policy.Boundary: I did not run cleanup, VACUUM, archive, delete, compression, config writes, dependency installs, service restarts, deploys, pushes, or clone/fetches during this benchmark.
Follow-up mitigation result from the same Windows RTX4090 profile.
I applied a conservative local mitigation to test whether reducing the active session JSONL scan set changes the benchmark:
~/.codex/sessions/**/*.jsonlfiles larger than 32 MiB~/.codex/sessionsinto a dedicated quarantine directorymanifest.jsonandRESTORE.mdResult:
~/.codex/sessions: 696 files / 1261.90 MiB -> 688 files / 457.29 MiBThis supports the earlier interpretation: bounding active session JSONL hydration materially reduces the local load in this profile. It is only a user-side mitigation, not a product fix, because it hides old histories from the active session path unless restored.
Follow-up correction on the user-side mitigation above:
After moving the 8 large JSONL files out of active
~/.codex/sessions, the user observed that even recent sessions were no longer visible in the Codex UI. I restored all 8 files back to their original active session paths and SHA256-verified the restored files.Post-restore checks:
~/.codex/sessions: back to 696 JSONL files / about 1262 MiBstate_5.sqlitethread rows all hadrollout_pathfiles present after restoreSo the quarantine experiment is useful as evidence that active JSONL scan-set size affects load, but it is not a safe user-side workaround for normal use because Codex Desktop appears to depend on those files being present for session visibility/navigation. The product-side fix still needs to be lazy loading / indexing / virtualization rather than asking users to move session files.
Follow-up after testing a safer user-side mitigation:
Manual filesystem quarantine is not a safe default workaround. I moved 8 old large JSONL files out of active
~/.codex/sessionsonly after copying and SHA256-verifying them, and it reduced the benchmark cost, but the user observed that even recent sessions disappeared from the Desktop UI. I restored all 8 files to their original paths and verified SHA256 matches. So removing/moving active JSONL files can preserve bytes but still break usability.Safer local mitigation used afterward:
codex archive <thread-id>for local threads older than 7 days.state_5.sqlitebefore the batch and wrote a restore manifest/runbook.codex_app.list_threads(limit=50)returned the recent local 7-day window again.Conclusion: official archiving is a much better user-side mitigation than moving JSONL files, but the product-side fix is still needed: thread list/startup/resume should not require parsing large active JSONL files, and transcript hydration should be bounded/lazy.
Correction / additional finding:
After running
codex doctor --json, I found that the earlier direct SQLite archive experiment left a parity warning:rollout_pathunder active~/.codex/sessions/...So direct DB updates are not a safe mitigation, even if recent threads appear again and data bytes are preserved. They can leave archived DB rows whose rollout files are still in the active sessions tree.
Current safer plan:
codex unarchive <id>thencodex archive <id>so Codex itself normalizes state and file placement.codex doctor --json, active thread list, and the benchmark before any all-thread repair.I also updated the metadata-only benchmark script to include
logs_2.sqlitewrite-rate sampling. Current latest sample still shows high-frequency log persistence:logs_write_sample: 10s, 1,969 rows, 196.86 rows/sTRACE logcodex.exe app-serverpeaked at about 130% of one coreConclusion: session JSONL archive reduces the largest parse bottleneck, but there are two remaining product-side issues: archive/file parity robustness and high-rate TRACE/DEBUG log persistence.
Follow-up on the local visibility regression after history-load mitigation:
state_5.sqlitehas 42 non-archived rows with existing files under~/.codex/sessions; 41 files have mtimes within the last 7 days, totaling about 106 MiB.codex doctor --jsonstill reports 499 archived DB rows whose rollout files remain under activesessionsrather thanarchived_sessions.threadshashas_user_event=0, including all non-archived recent rows, whilefirst_user_message,preview, andtitleare populated for all 42 active rows.has_user_eventor cached interactive-thread state, the UI can show no recent sessions even though the active rollout files are present.state_5.sqlitewhile Desktop/app-server are running. Any local repair is gated on closing Codex, copyingstate_5.sqlite,state_5.sqlite-wal, andstate_5.sqlite-shm, then applying a small pilot and re-runningcodex doctor.This is adjacent to the original load issue: user-side archiving can reduce JSONL parse cost, but the product needs a reliable metadata/index repair path so the list does not regress into an empty or stale state.
Additional local mitigation details after the visibility regression:
threads.has_user_eventflag is0for every row. A metadata-only scan of recent active JSONL files found user-like event types/roles in all recent active files checked; no message bodies or prompt text were exported.has_user_event=1only for recent non-archived rows whose JSONL structure contains user-like event types/roles;codex unarchive/codex archivecommands for rows whose DB archived flag and rollout file location disagree.codex doctor, restart Desktop, confirm recent sessions are visible, then proceed further only if the pilot is healthy.This reinforces the product-side need for a safe, built-in state/index repair path in addition to lazy/bounded history loading. User-side archiving can reduce parse load, but stale metadata flags can make the Desktop list look empty even when rollout files are present.
Follow-up from the same Windows Codex Desktop profile (2026-06-29 03:38 JST): recent sessions now appear to be hidden by thread metadata, not missing files.
Read-only health check:
threads: total 702, active 42, archived 660recent_active_has_user_event: 0 / 39recent_active_metadata_nonempty: 39 / 39 (first_user_message/preview/titlepopulated)has_user_event=0~/.codex/sessionsInterpretation: this looks like a state/index metadata regression. The recent session JSONL files still exist, but if Desktop filters/list-builds by
has_user_event, the UI can look empty even for sessions newer than 7 days.Local mitigation prepared but not applied live: a stopped-Codex repair pilot that backs up
state_5.sqlite+ WAL/SHM, verifies SQLite integrity, then updates onlythreads.has_user_event=1for recent active rows whose JSONL structural scan confirms user events. I am not mutating the DB while Codex Desktop/app-server is running.Follow-up local mitigation from the same Windows Codex Desktop profile (2026-06-29 03:53 JST): disabled MCP config did not stop already-running helper processes until I explicitly cleaned up the old app-server children.
Setup:
codex mcp list: chrome-devtools, playwright, filesystem, codebase-memory-mcp disabled in configapp-serverprocess tree and only matching disabled MCP server command patternsApply result:
Benchmark comparison, before -> after cleanup:
logs_2.sqlitewrite sample: 6.38 rows/s -> 0 rows/sThis suggests two related product improvements:
Follow-up after the disabled MCP helper cleanup: I also measured the remaining plugin helper footprint in the same Windows Desktop session.
After disabled MCP helpers were removed, Codex app-server still had 21
plugin-server-*child processes. Reading only process metadata/current directories showed:data-analytics: 7 processescreative-production: 7 processescodex-security: 7 processesThese were enabled plugin helpers and not needed for the current performance investigation, so after a read-only second review and an explicit dry-run selecting exactly 21 processes, I stopped only those three plugin groups. Rollback is restarting Codex Desktop.
Benchmark comparison before any helper cleanup -> after disabled MCP + optional plugin helper cleanup:
logs_2.sqlitewrite sample: 6.38 rows/s -> 0 rows/sFeature tradeoff: stopping enabled plugin helpers may make those plugin-backed tools unavailable until respawn/restart. Product-side improvement would be to lazy-start plugin helpers on first actual use, and terminate/reclaim idle helpers after a timeout, instead of keeping one process per server helper alive in the default Desktop session.
Additional local cleanup from the same Windows Desktop session: after disabled MCP and optional plugin helpers were stopped, six
node_repl.exeprocesses remained directly under the Codex app-server. They were not needed for this performance investigation, so after read-only review and confirming no in-flight MCP tool call, I stopped all six together.Result:
node_repl: 7 -> 0 in the benchmarked process kind countsTradeoff: browser/chrome/node-repl-backed tools may be unavailable until respawn/restart. Product-side improvement: avoid keeping an idle node_repl pool alive across sessions, or reclaim idle node_repl workers after a timeout.
Adding a Linux Desktop data point from 2026-06-30. I am running the official Codex Desktop bundle through the community Linux wrapper, so treat the platform layer with that caveat. The failure shape looks very close to the local-history / thread-list / hydration path described here.
Environment:
User-visible symptom:
Trigger pattern:
prod deploy.Representative process snapshot shortly after relaunch, while the app was usable again but still hot:
Local profile sizes from the same machine:
Log signals observed locally:
My read is that search/open of old local threads can push an oversized serialized thread payload through the app-server -> Electron renderer path. In this profile it manifested as a renderer/main/GPU hot loop and an unclickable Desktop window. This is adjacent to the unbounded local-history/list processing described in this issue, with thread search as the user-level trigger.
Expected behavior:
Adding a sanitized Windows data point at a smaller scale than the original report.
Environment:
Local state summary:
The renderer and app UI remained measurably active while idle. Because the currently opened thread was modest in size, the slowdown does not appear attributable only to rendering one exceptionally large transcript. The aggregate active-thread index, reconciliation work, spawn-edge state, or eager local-history hydration may also contribute.
This suggests the scalability threshold can become visible at hundreds of active threads combined with subagent relationships, not only at several thousand threads or one unusually large conversation.
Useful mitigations at the product level could include bounded sidebar projections, lazy hydration of inactive thread metadata, incremental reconciliation, and first-class bulk archive/cleanup controls.
Thread titles, identifiers, prompts, local paths, account information, and raw session files have been omitted.