Repository navigation
Description
What version of Codex is running?
- Codex Desktop
26.721.4979.0(native Windows, no WSL) - app-server:
codex-cli 0.146.0-alpha.3.1 - Bundled runtime:
cua_node/f8d2abcb7481383b
What operating system are you using?
Windows 11 x64, 24 GB RAM.
What is the issue?
node_repl.exe MCP server child processes accumulate over the life of the codex.exe app-server process and are never reaped. One new node_repl.exe appears roughly per opened conversation/thread, stays alive after the thread is idle or closed, and only goes away when the app-server itself exits.
Snapshot after ~8 hours of normal Desktop usage — 14 live node_repl.exe, all children of the single app-server PID, all idle (0 CPU since spawn):
ProcessId CreationDate Parent
17248 12:41 (app start) codex.exe (1772)
16888 13:12 codex.exe (1772)
19040 13:43 codex.exe (1772)
5508 15:18 codex.exe (1772)
21312 18:40 codex.exe (1772)
1668 19:02 codex.exe (1772)
21144 19:47 codex.exe (1772)
9004 19:58 codex.exe (1772)
20704 19:58 codex.exe (1772)
21984 20:04 codex.exe (1772)
20664 20:12 codex.exe (1772)
14232 20:18 codex.exe (1772)
20508 20:18 codex.exe (1772)
20588 20:19 codex.exe (1772)
Each holds ~7–9 MB working set and ~115–125 handles. Individually small, but they accumulate monotonically (verified creation timestamps map to thread-open events) and add up alongside the other per-thread state. Killing the app-server reaps all of them (verified: after terminating the app-server PID, node_repl.exe count went from 14 to 0).
Relevant config: [mcp_servers.node_repl] pointing at the bundled node_repl.exe, features.computer_use = true, features.multi_agent = true.
What is the expected behavior?
node_repl MCP processes for a thread should be terminated when the thread is closed/idle-expired, or at minimum pooled/reused, instead of leaking one process per thread until app exit.
Additional context
Possibly related to #30408 (per-thread MCP server processes never cleaned up), but this one is specifically the bundled node_repl on native Windows, where each thread spawns a distinct child that outlives the thread.
Activity
github-actions commentedon Jul 26, 2026
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
misosiruda commentedon Aug 5, 2026
Current Windows reproduction and lifecycle analysis
I reproduced the same process-retention pattern on a newer Windows Desktop build:
26.730.7989.0codex-cli 0.147.0-alpha.1.2At an earlier point in the same app-server lifetime I observed 7 direct
node_repl.exechildren using about 143.5 MiB combined. A fresh read-only capture at2026-08-05T13:56+09:00showed 4 direct children using 81.3 MiB and 532 handles. Three of the four used at most 0.02 CPU seconds during a three-second sample; one was active and owned a nested stdio app-server.This is an important distinction: these are not Windows PPID-orphans. They remain direct children of the live Desktop app-server (and were inside its Job in the earlier snapshot). Also, my count did decrease from 7 to 4 during the investigation, so my reproduction supports long-lived/logically retained runtimes, but does not support claiming that every instance is literally never reaped under every lifecycle path. Process inspection alone also cannot attribute every retained
node_repl.exeto a particular thread, status snapshot, hidden task, or other runtime owner.Relevant current-main behavior
I traced current
mainat5d89ab65dc9d4d0c55796c11df112b54157922b4.The per-thread path behaves as follows:
codex/codex-rs/app-server/src/request_processors/thread_lifecycle.rs
Lines 5 to 62 in 5d89ab6
thread/unsubscriberemoves the connection subscription but does not immediately shut down the thread:codex/codex-rs/app-server/src/request_processors/thread_processor.rs
Lines 887 to 912 in 5d89ab6
mcp_runtime.shutdown():codex/codex-rs/core/src/session/handlers.rs
Lines 587 to 609 in 5d89ab6
There is also a separate RPC/temporary-manager path that should not be conflated with thread subscription retention:
mcpServerStatus/list, resource reads, and tool calls withtokio::spawn:codex/codex-rs/app-server/src/request_processors/mcp_processor.rs
Lines 234 to 305 in 5d89ab6
codex/codex-rs/app-server/src/request_processors/mcp_processor.rs
Lines 391 to 480 in 5d89ab6
McpConnectionSet, then cancels its startup token and returns without explicitly awaitingshutdown():codex/codex-rs/codex-mcp/src/mcp/mod.rs
Lines 393 to 451 in 5d89ab6
taskkill /PID ... /T /F, but current code discards the command status:codex/codex-rs/rmcp-client/src/stdio_server_launcher.rs
Lines 373 to 386 in 5d89ab6
The evidence therefore points to at least two plausible retention mechanisms:
The first mechanism is consistent with the controlled subscribed-thread analysis reported in #20883:
#20883 (comment)
Relevant prior work that was not merged
Three earlier PRs overlap materially with this issue and should be evaluated before designing a new fix:
taskkillfailures, and added serialization coverage. It was closed as stale without being merged:Fix MCP status/list lifecycle leak #20447
[app-server] Reap orphaned idle threads on websocket disconnect #14997
[MCP caching]: pool MCP backends in thread manager #15258
These PRs target different layers, so none should be assumed to be a complete standalone fix for the current architecture. In particular, #20447 addresses detached temporary-manager/RPC lifetime, while #14997 and #15258 address per-thread retention and duplication.
Proposed fix direction
My preferred order would now be:
mcp_processor.rs:thread/closed, and preserve resume from the persisted rollout.Regression coverage should include:
The last item is currently a coverage gap: the existing real-process cleanup integration test is Unix-only:
codex/codex-rs/rmcp-client/tests/process_group_cleanup.rs
Lines 1 to 119 in 5d89ab6
Would maintainers prefer reviving the narrow #20447 lifecycle fix first, a Desktop subscription fix, or a combined staged approach? If one of those directions aligns with the intended lifecycle, I would be happy to prepare a small, test-first PR if invited.
vbzgn2rc98-ctrl commentedon Aug 17, 2026
Additional Windows reproduction on current Desktop
26.810.7004.0, with read-only lifecycle/performance measurements.This matches the lifecycle nuance already described above: the helpers are not literally never reaped. On this machine, a true idle period of roughly 30 minutes reclaimed most of a retained batch (
28 -> 7; 22/28 baseline helper PIDs exited). However, under sustained multi-thread Desktop use, helper creation outpaced that delayed cleanup and the process tree accumulated rapidly.Environment / current build
26.810.7004.026.810.52044Helper lifecycle observations
Across normal thread/task activity:
node_repl.exe:4 -> 6 -> 10 -> 12 -> 22 -> 23 -> 26 -> 2528 -> 7codex.execodex.exechildrenrmcp::service/rmcp::transport::child_processplus occurrences involvingfailed to terminate,failed to kill,orphan, andcleanupA particularly narrow trigger was observed: resuming another thread and asking only a design/conversation question (no requested local file, shell, browser, or computer action) increased
node_repl.exefrom10 -> 12. Previously observed helpers were not reclaimed at that point. This suggests helper/runtime creation can occur during thread/work-context initialization, not only after an explicit tool call.Performance degradation while active workload was running
During a later sustained-workload bad state, the root
codex.exeshowed a persistent cache-backed logical-read storm across three consecutive 5-second windows:265.752 MiB/s288.004 MiB/s286.842 MiB/s280.2 MiB/s288.0 MiB/s34k-37k/sAt the same time:
0-0.079 MiB/s0.24%011.04%, max19.36%41.7 GiB33.5%0.13%181,208/saverage, ~200,951/speaknode_repl.exe: idle baseline7, then22 -> 23after work resumedcodex.exeprivate memory grew from ~992 MiB -> 2263 MiBin ~10 minutes, with large short-window allocation/free churnUser-visible impact was system-wide mouse/input/window-animation stutter despite low total CPU, essentially idle physical disk, ample RAM, and low DPC activity. Fully exiting/restarting Codex cleared the bad state temporarily.
File-level trace follow-up
An 8.139-second PID-filtered kernel File I/O trace was later captured after the storm had subsided:
0799/3.297 MiBtotal (0.405 MiB/s)%USERPROFILE%\.codex\logs_2.sqliteandlogs_2.sqlite-wal, read in short ~2-second burstsThat calm-state trace did not reproduce the prior ~280 MiB/s process-level logical-read storm, so it does not attribute the storm to those SQLite files. The high-read state appears state-dependent/intermittent and would need another trace captured while the stutter is actively present.
Current interpretation
This machine supports:
codex.exelogical-read activity and a ~180k-200k/s context-switch storm;26.810.7004.0here.I am intentionally not uploading the raw ETL because it contains local workstation metadata/paths. Sanitized counters or another trace captured during the active bad state can be provided if maintainers need a specific additional measurement.
wakasellahi commentedon Aug 17, 2026
Additional Windows Desktop sample, non-urgent:
Counts declined throughout the sample, so this is not urgent or evidence of current growth. It still supports the lifecycle issue: child instances persist across task or tool activity rather than being retired per thread. No local process termination was used for this sample; the user will close and relaunch the app normally.
uto96322 commentedon Aug 18, 2026
Reproduced on a newer Windows Desktop build; this issue is still present.
Directly measured environment
10.0.19045(build 19045), x64OpenAI.Codex_26.814.5167.0_x64__2p2nqsd0c76g010976cua_node/2fb562745e6d66f0/bin/node_repl.exeObserved behavior
All observed
node_repl.exeinstances are direct children of the same long-livedcodex.exe app-server.node_repl.exeprocessesThis confirms the leak/retention also reproduces on Windows 10 and on Desktop build
26.814.5167.0, not only the older Windows 11 build reported in the original issue.The same user separately observes a more severe reproduction on a Windows 11 home PC: the count reportedly increases on every chat turn. Disabling
node_repl.exeprevents the accumulation, but Browser/Web work and Computer Use then stop functioning, so disabling the runtime is not a viable workaround.Expected
The process count should remain bounded through reuse/pooling, or thread-scoped runtimes should be reaped after the owning turn/thread becomes idle. Long-lived Desktop sessions should not monotonically accumulate
node_repl.exechildren.A full app-server restart remains the only reliable cleanup observed.
grtninja commentedon Aug 24, 2026
Additional sanitized Windows observation from newer builds:
codex.exeprocess had 11 direct bundlednode_repl.exechildren.cmdprobes from the task failed before launch with Win32 error 1816 (ERROR_NOT_ENOUGH_QUOTA).The direct
codex.exe -> node_repl.exeshape appears related to this report, but this snapshot does not map a child to a specific open or closed thread and therefore does not prove missed cleanup. Is there a supported diagnostic field or procedure for correlating each bundlednode_replchild with its owning thread/session and terminal cleanup event?twk0672005 commentedon Aug 26, 2026
Additional native Windows Desktop reproduction on
OpenAI.Codex 26.818.8289.0. This measurement is from the Desktop app-server, not the VS Code extension.Read-only capture at
2026-08-26T00:21:49Z:codex.exe app-server, started at2026-08-25T15:50:19Z;node_repl.exeprocesses, all direct children of that app-server;node_repl.exeworking set: approximately 110 MiB;No processes were terminated.
This is a point-in-time persistence/accumulation snapshot. It confirms the direct parent-child shape on the current native Desktop build, but it does not map each child to a specific thread or prove that every individual child missed its cleanup deadline.
For a narrow first fix, I would favor re-evaluating the lifecycle intent of #20447 on current main before attempting MCP pooling:
shutdown().awaittemporaryMcpConnectionSetowners on success, error, and cancellation;The subscribed-thread retention path can then be handled separately: Desktop should unsubscribe background threads it no longer needs, and idle unload must protect active turns and pending requests while preserving resume from the rollout. Project/app-server MCP pooling seems better treated as a later architecture change, not a substitute for deterministic teardown.
Is there a supported diagnostic field or procedure for mapping each bundled
node_replchild to its owning thread/request and terminal cleanup event? I can provide the sanitized health JSON or run a controlled create/close/reap correlation test if maintainers specify the intended procedure.Rober1208 commentedon Oct 5, 2026
Same here on Windows: each conversation opened in the desktop app keeps its own
node_repl.exe, together with the rest of its MCP set, until the app server exits.Until there is a fix, I made a small tool that lists these per-conversation copies, with the conversation title, memory and idle time, and stops the idle ones: mcp-janitor
By default it keeps the servers of the conversation you used last (Codex cannot restart a stopped one), and
stop --idle 1h --dry-runshows what it would stop without stopping anything. Reports from other Windows setups would help a lot; please open an issue if it misses or mislabels something.rumours86 commentedon Oct 14, 2026
Same on Linux with the desktop app (
chatgpt26.1007.21434 .deb, Linux Mint 22.3 / Ubuntu 24.04 base, kernel 7.0).One
codex app-server(started by the app ascodex -c features.code_mode_host=true app-server --analytics-default-enabled -c plugins.codex-app-tools@openai-bundled.mcp_servers.codex_app.enabled=true -c plugins.code-review@openai-bundled.mcp_servers.code-review.enabled=true ...)78 minutes after the app was started:
cua_node/bin/node ./server.mjscua_node/.../@oai/cua-repl/bin/cua-repl.mjs(each with its ownnode_replchild)cua_node/bin/node_repl(direct)Together these hold 682 MB (PSS + swap), 590 MB of it in processes older than 30 minutes that use no CPU.
The same app-server at 238 minutes: 16 ×
server.mjs(14 older than 30 min), 8 ×cua-repl(7), 8 ×node_repl(7) - 994 MB, 871 MB of it in the old idle ones. It only grows. They are direct children of the live app-server, not orphans, so they disappear only when the app quits. On a 16 GB laptop this regularly pushes the system into zram reclaim; the same pattern was described in #46833 (Windows) and #39816 (macOS).A new
server.mjs/cua-replpair appears for almost every new thread and is never shut down when the thread finishes or is left idle. An idle timeout for a thread's MCP set (or tearing it down when the thread is unloaded/archived) would fix this on all three platforms.charonwang commentedon Oct 30, 2026
Additional Windows observation on a newer Desktop build (2026-10-30).
Environment:
OpenAI.Codex_26.1023.6621.0_x64.codex.exe app-server, started on 2026-10-29 at 18:47 (UTC+08:00), withcode_mode_hostenabled.cua_node/.../bin/node_repl.exeexecutable.Read-only process snapshot at 2026-10-30 09:37 (UTC+08:00):
node_repl.exeprocesses, all descending from that one app-server. Some are direct children; others are below anode.exechild of the same app-server.This supports long-lived bundled worker retention on the newer build, but it is a point-in-time observation, not a controlled create-thread / close-thread / reap test. The sample does not show monotonic growth, establish that every worker has exceeded its intended lifetime, or prove that these workers cause UI lag.
The available local diagnostics did not expose a worker-PID-to-owning-thread mapping or the corresponding teardown events. No processes were terminated and no settings were changed during this inspection.
Is there a supported diagnostic procedure for mapping each bundled worker PID to its owning thread/request and checking its expected cleanup deadline? That would help produce a controlled lifecycle reproduction rather than relying on process counts alone.