Repository navigation
[Linux Desktop] Ambient suggestions/prewarm leak MCP and node_repl stacks every ~5 minutes #44525
Description
Summary
On the official ChatGPT/Codex Linux desktop package, leaving the app running for several days causes unbounded memory growth because periodic desktop-owned thread/start / startup_prewarm sessions create new node_repl and bundled MCP helper process stacks that are not reaped.
In this incident, the single long-lived Codex app-server accumulated 1,082 helper processes and approximately 12.65 GiB PSS plus 5.19 GiB swap. The system reached 18/22 GiB RAM and 8/8 GiB swap. Fully terminating the Codex scopes immediately returned the machine to about 6–7 GiB used RAM.
This looks like a Linux reproduction/variant of #12491, #25015, #30408, and the ephemeral-session lifecycle described in #39783. The apparent trigger here is periodic ambient suggestions/startup prewarming rather than user-created subagents.
Environment
- ChatGPT/Codex Desktop RPM:
chatgpt-26.901.41123-1.aarch64 - Bundled Codex CLI:
codex-cli 0.153.3 - OS: Oracle Linux Server 10.2, aarch64
- Kernel:
Linux 6.12.0-205.92.4.2.el10uek.aarch64 aarch64 - Physical memory: approximately 22 GiB
- Swap: 8 GiB
- Desktop: GNOME Wayland headless session
- Subscription: not included; this appears independent of plan/account tier
Relevant configuration before mitigation:
[desktop]
ambient-suggestions-enabled = true
[features]
js_repl = false
[mcp_servers.node_repl]
command = "/usr/lib/chatgpt/resources/cua_node/bin/node_repl"
startup_timeout_sec = 120The bundled Browser, Chrome, codex-app-tools, and unified-computer-use plugins were enabled.
What issue are you seeing?
The desktop app's long-lived app-server repeatedly starts fresh local MCP/helper stacks and retains the old ones indefinitely.
At capture time:
537processes with commMainThread538processes with commnode_repl1,082processes in the ChatGPT/Codex app-server tree- app-server tree PSS:
12,652.8 MiB - app-server tree private memory:
12,508.4 MiB - app-server tree swap:
5,190.7 MiB - systemd scope
memory.current:13,147,430,912bytes - systemd scope
memory.swap.current:5,010,042,880bytes - systemd scope task count:
8,643
The app-server itself had grown to approximately 3,289.6 MiB PSS and 547.3 MiB swap.
The retained helper groups had this shape:
ChatGPT
└─ codex ... app-server
├─ node ./server.mjs
├─ node .../unified-computer-use/.../scripts/launch.mjs
│ └─ node_repl
└─ node_repl
Most helpers were direct children of the same app-server. New groups appeared at roughly five-minute intervals. During sustained reproduction, each hour added approximately 22–24 MainThread processes and 22–24 node_repl processes.
The local logs_2.sqlite correlated each creation cluster with a new desktop request and a new ephemeral thread ID:
app-server request: thread/start
thread_start.create_thread
thread_spawn
session_init
startup_prewarm
mcp.runtime.refresh:new{server_name=cua_repl}
mcp.runtime.refresh:new{server_name=node_repl}
mcp.runtime.refresh:new{server_name=codex_app}
mcp.runtime.refresh:new{server_name=codex_apps}
No user turn or MCP tool call was required for the periodic prewarm sessions.
A normal SIGTERM to the main ChatGPT PID closed the main UI process, but the codex app-server and hundreds of helpers remained alive after being reparented to the user systemd process. Terminating the two Codex-owned systemd scopes was required to remove all retained processes.
This was not GNOME or filesystem cache pressure: the user GNOME Shell was approximately 237 MiB PSS at diagnosis, while terminating Codex reduced used RAM from about 18 GiB to 5.9 GiB immediately.
Steps to reproduce
- Install and launch the Linux ChatGPT/Codex desktop package.
- Enable the bundled Browser/Computer Use plugins and leave ambient suggestions enabled.
- Keep the app and an existing project open for an extended period. User activity does not appear necessary once the periodic behavior starts.
- Monitor helper counts:
watch -n 30 'pgrep -xc MainThread; pgrep -xc node_repl; free -h'- Inspect
~/.codex/logs_2.sqliteand correlate process birth times withthread/start,startup_prewarm, andmcp.runtime.refreshrecords. - Observe a new helper stack approximately every five minutes and no return to baseline.
- Leave the application running for one or more days; RAM and swap eventually become exhausted.
- Fully terminate the ChatGPT/Codex app-server scope; memory returns immediately.
Mitigation and A/B result
The following was changed:
[desktop]
ambient-suggestions-enabled = falseAfter fully terminating all old Codex scopes and relaunching the desktop app, I monitored it for six minutes, covering the previous five-minute creation interval:
codex=1 MainThread=0 node_repl=0
used RAM remained approximately 6.96–7.02 GiB
available RAM remained approximately 15.39–15.45 GiB
No new helper stack appeared during that interval. This strongly associates the periodic trigger with ambient suggestions/prewarming, while the unbounded impact is caused by missing teardown of the inherited MCP runtime.
Expected behavior
- Desktop-owned ambient-suggestion/prewarm threads that do not need tools should start with MCP servers disabled.
- Completion or abandonment of an ephemeral/prewarm thread should shut down its session-owned MCP runtime and child process group.
- Periodic background work must return process count and memory to a bounded baseline.
- Fully quitting ChatGPT Desktop should terminate the app-server and all helper descendants.
- A long-running idle desktop session should not require periodic manual restarts to avoid system-wide memory and swap exhaustion.
Additional information
Raw SQLite/session files are not attached because they contain prompts, local paths, and private project metadata. Sanitized counts, timestamps, process relationships, and log signatures are included above. I can provide a more targeted diagnostic if maintainers specify the required fields.
Activity
github-actions commentedon Sep 10, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
johnpippett commentedon Sep 14, 2026
Additional Linux evidence: system memory exhaustion on Desktop 26.908.40834
This report adds a systemd out-of-memory event and local process measurements. The results are consistent with this issue, but they do not prove the same internal cause.
Environment
openai-codex-desktop 26.908.40834-1. Version checked from the installed package, not the About dialog.codex-cli 0.154.0-alpha.6.2.4.0.3-1, Arch Linux, x86-64.7.2.3-arch1-3; Hyprland:0.56.2-2, Wayland.User observations
I often leave Codex open for long periods. After I disconnected an external monitor and opened the laptop, the whole desktop was very slow. I closed and opened the lid again. The system later recovered. Codex also stopped at its startup logo during this incident. A later app restart restored its interface.
This was not a controlled reproduction. The monitor and lid actions are context, not established causes.
Confirmed local evidence
The system journal records the following events on 2026-09-14. Times are local, UTC-07:00.
thread/startrequest withturn_trigger: Some("ambient_suggestions").startup_prewarmfor that thread.systemd-oomdselected the application scope for termination. System RAM and swap use exceeded its 90% threshold.The selected scope was named
app-org.chromium.Chromium-2975.scope. Its recorded peaks were 25.6 GiB memory and 51.7 GiB swap. These are separate peak values, not a simultaneous total. systemd reported:The scope name is important: the restarted ChatGPT executable also runs in an
app-org.chromium.Chromium-<PID>.scope. This label does not identify a separate browser session.About 84 minutes after the restart, the new scope contained 135 processes and used approximately 6.9 GiB according to
memory.current. Process inspection found 23node_replprocesses and 11 copies each of several different helper servers. This is a snapshot, not a measured growth rate. It does not establish that each helper was unused.Mitigation and limits
We backed up
~/.codex/config.tomland added:The configuration parses correctly. No other settings changed. At the time of this report, we have not completed a restart and observation period after this change. We cannot yet report that the mitigation works here.
There is no process-by-process memory capture from before the old scope was terminated. The ambient task may have added load to an already overloaded app. The exact source of the earlier accumulation remains unconfirmed. Slow resume and separate Chromium crash notifications also need further diagnosis.
Expected behavior and question
Background tasks should release helper processes when those processes are no longer needed. Leaving the app open should not exhaust system memory.
Is there a fix planned for a desktop release, or a supported mitigation beyond disabling ambient suggestions and fully restarting the app?
Private paths, prompts, thread identifiers, and raw databases are omitted.
This comment was prepared with the assistance of AI. Local measurements and limits are stated above.
stephenphilipjensen-ship-it commentedon Sep 18, 2026
Same leak reproduced on Windows. It is not Linux-specific.
Environment: Windows 11 Pro (build 26200), Codex Desktop client 26.911.61220 (MSIX
OpenAI.Codex_26.915.3509.0), ChatGPT.exe 153.0.8010.48, codex-command-runner 0.155.0-alpha.9, 32 GB RAM.Observed after 7.7 h uptime (one app-server,
codex.exechild ofChatGPT.exe):node_repl.exe, 118 bundled cua_nodenode.exe(cua-repl.mjsand codex_app toolsserver.mjsviacmd.exe), 104 systemnode.exefor plugin MCPs (./mcp/server.mjsfrom openai-developers,./mcp/server.bundle.mjsfrom creative-production), and 12codex.exeundernode_repl. In total about 10 GB working set.Amplifier: Editing
config.toml(disabling two plugins) causedconfig/batchWrite, then 39thread/start, 8thread/resumeand 19model/listwithin one minute. About 150 new MCP processes were spawned. Killing idle MCP processes of loaded threads just gets them respawned viamcp.runtime.refresh_if_dirty.Also in
logs_2.sqlite: Repeated MCP startup timeouts (node_replhandshake 120 s,cua_repl100 s,codex_app10 s, plugin servers 30 s) add latency to new turns. A remote plugin that never completed OAuth (cloudflare-api) was retried 66 times in 10 days.Workaround: Disable unused plugins. Restart the app-server when idle, because the desktop app respawns it and the UI survives. Run an hourly job that kills MCP helpers older than 3 h with no CPU use.
Expected: Helpers are shared across threads or shut down when a thread or ephemeral session is unloaded. A catalog refresh should not spawn per-thread stacks. A plugin that failed OAuth should not be retried on every thread start.
84dnnvbdvp-debug commentedon Sep 18, 2026
There is a fairly sharp lifecycle discriminator in the exact backend version from the new Windows report.
rust-v0.155.0-alpha.9resolves to434535bddfaf405a032f57be3c1096dd25ff6312. In that release:SessionServices::mcp_runtimeis documented as “the single owner of live MCP connections for this thread”, so MCP connection ownership is deliberately per-thread.thread_unsubscriberegression explicitly asserts that a subscribed, idle thread stays loaded even withthread_unload_delay_secs = 0. The unload countdown begins only after the last subscriber is gone.mcp_runtime.shutdown().So “thread is idle” is not the backend teardown boundary; “thread is idle and has no subscribers” is. That suggests a narrower discriminator for the observed ~one helper set per loaded thread: if Desktop keeps a subscription on every thread it has loaded, the backend will intentionally retain each thread’s MCP runtime indefinitely. That would fit the reported per-loaded-thread scaling without requiring a leak inside
McpConnectionSetitself. This does not prove Desktop is retaining those subscriptions; that client state is the remaining branch to test.A deterministic regression could make that distinction cheaply:
thread/unsubscribeN-1 of them and wait past the unload delay; requirethread/closed,thread/loaded/listto drop them, andmcp.connections.live/ child-process count to fall accordingly.If step 2 tears the helpers down correctly on the reported backend, the high-value fix boundary is likely Desktop subscription/liveness policy (or decoupling event subscription from full MCP-runtime retention), not global MCP sharing. If the helpers survive
thread/closed, then the teardown path itself is leaking despitemcp_runtime.shutdown().0xdevalias commentedon Sep 22, 2026
Related but distinct observation on macOS Desktop 26.903.71938 / Codex 0.153.4: I found 20 loaded sessions with
ephemeral=true, sourcemcp_extension_host, no turns/path/parent, cwd/, and no state-DB, live-window DOM, or spawn-edge presence. Stopping the 17-day-old Chrome extension native host did not unload them after >60 s, so the visible native host was not their current owner; an internal Desktop/app-server owner retained them.Separately, logs showed a focus-triggered bundled-plugin reconciliation restart MCP fleets across the already-retained session set in a tight burst. That explains the apparent mass respawn, but not the original owner leak. I have not tested this issue's ambient-suggestions mitigation, so I would not generalize that workaround to this cohort. A final-owner
thread/unsubscribecan unload a task after ~60 s (confirmed once), but these internally owned ephemeral sessions require subscriber diagnostics or explicit unload support.yuzhu-oai commentedon Oct 2, 2026
Fixes have been merged and will be released soon.