Codex for macOS accumulates Computer Use / MCP helper processes and unreaped zombie children, causing HID lag and WindowServer/TCC stalls
Codex macOS Resource Leak / Unreaped Child Processes Report
GitHub Issue Draft
Title
Codex for macOS accumulates Computer Use / MCP helper processes and unreaped zombie children, causing HID lag and WindowServer/TCC stalls
Summary
On macOS, a long-running Codex session accumulated a large number of Codex-owned helper processes related to Computer Use / MCP tooling. The machine became visibly laggy. Inspection showed:
- Dozens of live
SkyComputerUseClient mcpprocesses. - Dozens of live
node_replprocesses. - Dozens of live
codex app-server --listen stdio://helper processes. - More than 100 zombie child processes whose parent was the main Codex GUI process.
- macOS spin reports for Codex showing slow HID event handling, with WindowServer blocked in the TCC event-monitor permission path.
Manual cleanup of stale helper processes reduced live Codex-related memory use by roughly 3 GB, but the zombie processes could not be cleared without restarting Codex because the main Codex process did not reap them.
Related Issues
This may share root cause with MCP stdio lifecycle leaks such as #17832, but this report adds a separate Computer Use case with SkyComputerUseClient accumulation, Codex-owned zombie children, and macOS WindowServer/TCC HID stalls. It does not look like the SkyComputerUseClient code-signing crash in #21200 or the simple unexpected-exit case in #18549.
Environment
Sanitized system details:
- App: Codex for macOS
- Codex version:
26.527.31326 - Codex bundle version:
3390 - Codex bundle ID:
com.openai.codex - Chromium base:
148.0.7778.179 - Computer Use app bundle ID:
com.openai.sky.CUAService - Computer Use app version:
1.0 - Computer Use build:
799 - Computer Use client bundle ID:
com.openai.sky.CUAService.cli - Computer Use client version/build:
1.0 (799) - OS: macOS
26.5build25F5042g - Architecture: Apple Silicon /
arm64 - CPU: Apple M-series Pro class, 14 logical CPUs
- Memory: 24 GB
- Thermal state: no thermal or performance warning recorded during sampling
Privacy redactions applied:
- User home path replaced with
<HOME>. - Host/device name redacted.
- Project/workspace paths omitted.
- Unrelated app/process names omitted unless directly relevant.
- Raw spin report should not be attached unless separately redacted; it contains a full process inventory.
Observed Behavior
The main Codex process stayed alive for approximately 16 hours. During that period, helper processes accumulated rather than being cleaned up.
Initial process counts observed:
SkyComputerUseClient count=50 cpu=18.0% mem=4.7% rss=1448.1 MB
node_repl count=47 cpu=0.0% mem=0.0% rss=405.3 MB
codex app-server stdio count=47 cpu=3.2% mem=6.3% rss=1715.4 MB
Codex app/server/renderers count=several, additional RSS/CPU
Aggregate Codex-related footprint before cleanup:
codex_related count=143-144
codex_related RSS ~= 4.8-4.9 GB
codex_related CPU ~= 28-70% depending on sample
Zombie processes:
zombies=126
parent=<codex-main-pid> zombies=126
After killing stale helper processes, the zombie count increased slightly rather than disappearing:
zombies=128
parent=<codex-main-pid> zombies=128
That strongly suggests the main Codex process is not reaping exited child processes.
System Symptoms
The machine was visibly laggy. System-level sampling showed:
Load Avg: 8.12 6.57 6.23
Processes: 980 total
Threads: 6301
CPU usage: 13.70% user, 22.98% sys, 63.30% idle
PhysMem: 22 GB used, 1.3 GB unused
Compressor: ~5.5 GB
Memory pressure: not critical
Thermal warnings: none
The strongest live CPU consumers during the bad state included:
trustd ~90-100% CPU in multiple samples
syspolicyd ~20-60% CPU in multiple samples
WindowServer ~40-50% CPU in multiple samples
Codex GPU ~8-40% CPU depending on sample
Codex renderer/app server also periodically high
After helper cleanup, trustd and syspolicyd dropped back to low single-digit CPU in later samples. This suggests the large helper-process population may have been amplifying code-signing/trust/TCC work.
macOS Spin Report Evidence
macOS generated at least two Codex-related spin reports during the session.
Recent spin report summary, sanitized:
Date/Time: 2026-05-31 17:51:29 local time
OS Version: macOS 26.5 (Build 25F5042g)
Reason: Slow response to HID event
Command: WindowServer
Affected Process: Codex [<codex-main-pid>]
Event: spin
Duration: 2.98s
Duration Sampled: 2.77s
Steps: 277 (10ms sampling interval)
Report threshold: 0.5s
Active CPUs: 14
Memory size: 24 GB
Memory pressure: average 40%, highest 42%
Available memory: average 14.26 GB, lowest 13.75 GB
HID Events: LeftMouseDown*, LeftMouseUp
The heaviest stack for the target process was in WindowServer waiting on TCC event-monitor access checks:
WindowServer
SLXServer
server_loop
CGXRunOneServicesPass
post_port_data
rendezvousHandler
CGXHandleMessageWithResponseBuffer
_XGetEventShmem
WSEventSourceCreateGraphShmemForAuditToken
WSAuditTokenCanMonitorEvents
WSAuditTokenHoldsCachedProcessRight
audit_token_check_tcc_access
TCCAccessPreflightWithAuditToken
TCCAccessRequest
tccd_send_message
xpc_connection_send_message_with_reply_sync
dispatch_mach_send_with_result_and_wait_for_reply
mach_msg2_trap
The slow HID event line:
CGXGetNextEvent 1-274 3000.000ms WindowServer thread
There was another earlier Codex spin report the same day:
Affected Process: Codex
Event: spin
Duration: 1.28s
WindowServer stack again included kTCCServiceListenEvent / TCC preflight path
Nearby TCC logs also showed repeated privacy preflights for screen/event access by a non-Codex screen/window utility. That may have amplified the symptom, but it does not explain the Codex-owned process leak or the Codex-owned zombies.
Cleanup Experiment
The session was kept alive by not killing:
- The main Codex GUI process.
- The main Codex
codex app-server --analytics-default-enabledprocess.
Stale helpers were terminated carefully:
Killed 40 stale SkyComputerUseClient mcp processes.
Killed 80 stale node_repl / codex app-server --listen stdio:// helper processes.
Killed 1 stale SkyComputerUseService process.
Killed several old orphaned Codex crash/keyboard helper processes from previous app launches.
After cleanup:
codex_related count ~= 15-18
codex_related RSS ~= 1.8-1.9 GB
SkyComputerUseClient herd gone
node_repl / stdio helper herd gone
trustd / syspolicyd CPU substantially lower
zombies still present under main Codex process
A single fresh helper triad later respawned:
node_repl
SkyComputerUseClient mcp
codex app-server --listen stdio://
That may be the active/current session helper set. The bug is not that one helper set exists; the bug is that many old helper sets accumulated and were not cleaned up.
Expected Behavior
Codex should:
- Keep at most the necessary active helper processes for currently active tools/sessions.
- Tear down stale
SkyComputerUseClient,node_repl, and stdiocodex app-serverhelpers after a tool/session completes. - Reap exited child processes so zombie children do not accumulate.
- Avoid triggering system-wide TCC/trust/code-signing churn due to stale helper population.
- Avoid causing WindowServer HID stalls when Computer Use/event-monitor permissions are involved.
Actual Behavior
Codex accumulated:
- Approximately 50 Computer Use client processes.
- Approximately 47
node_replprocesses. - Approximately 47 stdio
codex app-serverchildren. - 126+ zombie children owned by the main Codex process.
The system became laggy and macOS generated Codex spin reports involving WindowServer/TCC event-monitor checks.
Repro / Diagnostic Commands
These are safe commands to run locally. Redact output before posting publicly.
Count Codex-related helpers:
ps -axo pid,ppid,pgid,stat,%cpu,%mem,rss,etime,args |
awk '/Codex|SkyComputerUse|node_repl|codex app-server/ {print}'
Aggregate Codex-related footprint:
ps -axo pid,ppid,pgid,stat,%cpu,%mem,rss,etime,args |
awk '/Codex|SkyComputerUse|node_repl|codex app-server/ {
cpu += $5; mem += $6; rss += $7; n++
}
END {
printf "codex_related count=%d cpu=%.1f mem%%=%.1f rssMB=%.1f\n",
n, cpu, mem, rss/1024
}'
Count zombie children by parent:
ps -axo stat,ppid |
awk '$1 ~ /^Z/ { z++; p[$2]++ }
END {
print "zombies=" z+0
for (ppid in p) print "parent", ppid, "zombies", p[ppid]
}'
Inspect top CPU processes:
ps -axo pid,ppid,%cpu,%mem,rss,etime,args |
sort -nrk3 |
head -30
Sanitize paths before sharing:
sed -E 's#/Users/[^/]+#<HOME>#g; s#[[:alnum:]_.-]+_2026-[0-9-]+-[0-9]+_[^./]+\\.spin#<redacted-spin-file>.spin#g'
Workaround
The clean workaround is to quit and reopen Codex. That should clear:
- Live stale helpers.
- Zombie processes owned by the old Codex parent.
If keeping the current session alive is required, stale helper processes can be killed manually, but this does not clear zombies because the main Codex process still fails to reap them. Killing children may actually increase the zombie count until Codex is restarted.
Suggested Fix Area
Likely areas to inspect:
- Lifecycle ownership for Computer Use MCP client processes.
- Lifecycle ownership for per-session
node_replprocesses. - Lifecycle ownership for stdio
codex app-server --listen stdio://child processes. - Child-process reaping in the main Codex macOS process.
- Cleanup path when a thread/tool session ends, errors, times out, or is superseded.
- Interaction between Computer Use/event-monitoring permissions and TCC preflight calls.
Severity
High for long-running desktop sessions:
- Causes visible system lag.
- Accumulates multiple GB of resident memory.
- Creates unreaped zombie children.
- Triggers macOS spin reports.
- Involves WindowServer/TCC HID event stalls, so user input latency is affected.
Notes For Maintainers
I can provide raw spin reports privately if needed, but they contain a full process inventory and local paths. The issue body above intentionally omits those details while preserving the relevant stack, counts, versions, and cleanup experiment.
10 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Reviewed the suggested duplicates. I do not think this is an exact duplicate.
Closest is #25719 because it also involves macOS
syspolicyd/trustdpressure around Codex Desktop / Computer Use. This issue adds separate lifecycle evidence: accumulatedSkyComputerUseClientprocesses, duplicatenode_repl/codex app-server --listen stdio://helpers, 126+ Codex-owned zombie children, and WindowServer/TCC HID spin reports.The Windows/Linux MCP issues (#25697, #24347, #25742) look related at the lifecycle class level, but this report is specifically macOS Codex Desktop + Computer Use + unreaped children. #25667 is also adjacent, but focuses on
code_sign_clonetemp dirs after quit.Adding current-build evidence from another macOS machine. This still reproduces on the current Sparkle appcast build and looks like the same lifecycle/perf class as this issue, with one extra signal: repeated
AppServerConnectioninitialize/reconnect failures while Computer Use MCP helpers fan out during ordinary tool discovery.Environment:
26.602.40724, bundle build359326.602.40724/3593, publishedFri, 05 Jun 2026 17:00:22 +0000codex-cli 0.135.0computer-use@openai-bundled 1.0.809com.openai.sky.CUAService, version/build1.0 (809)26.3build25D125,arm64Negative checks:
~/.codex/config.toml.SKY_CUA_SERVICE_PATHpoints at<HOME>/.codex/plugins/cache/openai-bundled/computer-use/1.0.809/Codex Computer Use.app.state_5.sqliteandlogs_2.sqliteboth passpragma integrity_check.com.openai.codexandcom.openai.sky.CUAService.Current process inventory, without restarting/killing Codex:
The oldest Computer Use MCP/helper family was ~71 minutes old; two more were ~35-36 minutes old; newer helper families appeared during this diagnostic even though no Computer Use desktop action was invoked. That suggests the Computer Use MCP process is being started as part of tool listing/session setup, not only as part of actual Computer Use actions.
High-signal log evidence:
Earlier in the same long-running app session:
There is also a Computer Use MCP protocol-compat warning in the SQLite logs:
I think the user-visible symptom is "Computer Use feels flaky", but the local root-cause shape looks broader:
This does not look like the plugin-missing issues (#18258, #21579, #26429) on this machine. It looks closer to this issue plus the app-server/MCP timeout family.
Feedback ID / Bug Report ID:
019eb57d-d318-78f2-8c45-76dea130046d---
A brief summary of what seems to be related issues that I just noticed due to my system being at its process limit, so failing to even run programs anymore:
The most important maintainer-facing points (according to Codex, GPT 5.5 (high)) are:
---
Summary of seemingly related issues by Codex (GPT 5.5, high) while reviewing this:
Other potentially useful/relevant threads for maybe more context:
---
Full overview / summary of everything tried/explored as per Codex (GPT 5.5, high):
Codex Desktop zombie-process accumulation and macOS process-table exhaustion
Executive summary
On June 11, 2026, a long-running Codex Desktop process on macOS had accumulated
approximately 9,940 unreaped zombie children. The zombies consumed nearly all
of the per-user process-table allowance:
This directly caused ordinary process creation to fail:
The evidence establishes that:
of process slots and did not remove the zombies.
SIGCHLD, including afterSIGCONT, did not make Codex reap them.the zombie count was zero.
The evidence does not establish:
the exited children only as
<defunct>;count was measured immediately after restart with almost no subsequent use;
node_repl, Computer Use, or stdio app-server helpers werenecessarily the zombie source, although their accumulation makes them
plausible lifecycle-related contributors.
This is best treated as a severe additional reproduction of
openai/codex#25744, with a
stronger demonstrated impact: complete per-user process-table exhaustion.
Environment
Public-safe environment details:
Redactions used below:
PIDs are retained where useful because they are ephemeral and essential for
understanding parent-child relationships.
Timeline and observations
1. Initial process-limit diagnosis
The first diagnostic set checked OS details, launchd/kernel limits, process
counts by user, and process counts by executable.
Command:
Relevant output:
Interpretation:
kern.maxprocperuid.2. Identification of the zombie parent
Command:
Relevant output:
The parent was then identified:
Output:
Direct-child sampling:
Representative output:
The oldest sampled zombies were almost as old as the Codex process itself.
This indicates gradual accumulation across the long-running app session rather
than a single recent burst.
At this point even a simple diagnostic pipeline failed:
3. Killing live helper processes did not solve the limit
Dozens of live Codex-related backend/helper processes were manually terminated.
The follow-up checks were:
Relevant output:
Live helper counts had dropped substantially:
Interpretation:
wait()/waitpid(), or by the parent exiting.The top-zombie-parent check made the concentration explicit:
Relevant output:
The few one-zombie
node_replparents were negligible compared with theapproximately 9,936 zombies directly parented by the main GUI process.
4.
SIGCHLDdid not trigger reapingCommands:
Output:
Retry after continuing the process:
Output:
Interpretation:
SIGCHLD.main process exit or injecting reaping behavior into it.
5. Final pre-restart snapshot
The saved
codex-zombie-process-snapshot.txtwas captured later in the samebad state.
Relevant output:
The snapshot also showed live Codex-related helpers:
This is useful supporting context, but the zombie command names had already
collapsed to
<defunct>, so it cannot prove that any of these helper classeswere the exited children represented by the zombie entries.
6. Main-process sample
Command:
Sample metadata:
The sampled main thread and many workers were predominantly waiting in
event-loop primitives such as
mach_msg2_trap,kevent, anduv_run.No
waitpid,wait4,SIGCHLD, child-exit, or explicit reaping stack wascaptured.
This sample is not proof of the missing code path: a 10-second statistical
sample cannot prove that a function never runs. It does show that no active
reaping loop was observed while nearly 10,000 zombies were pending.
The 10.6 GB process footprint is independently notable, but zombies themselves
do not account for that memory. Memory retention and child reaping should not
be conflated without more profiling.
7. Saved unified-log exports
Two log exports were saved:
The commands used were:
and:
Review result:
The exports contain thousands of ordinary Codex/macOS framework messages, but
no direct spawn-failure line matching the predicates above.
This negative result matters:
ps,kern.maxprocperuid, and the shell's fork failure;Codex spawn exception;
account-service activity, and other private system context, so it should not
be attached publicly without heavy redaction.
8. Session-preservation note
codex-session-restore.txtwas saved before restart to preserve a manual listof open Codex windows and thread URLs. It contains 12 thread identifiers and
project/task titles.
It explains why preserving the running app initially mattered, but it does not
identify the leak. The raw file should not be posted because thread IDs,
workspace topics, and project names are private.
9. Update/restart recovery
The Codex update changed the main process from PID
19100to PID72552.Commands:
Relevant output:
The post-restart attachment summarized in the shared chat additionally reported:
Interpretation:
26.608.12217fixed recurrence,because the replacement process had just started and had barely been used.
Root-cause assessment
Proven
The main Codex GUI process failed to reap thousands of exited direct children.
Those zombie entries accumulated over an approximately 11-day app lifetime and
eventually exhausted
kern.maxprocperuid.At the OS level, the missing invariant is:
Strongly suggested, but not proven
The broader Codex helper lifecycle likely contributes to the rate of child
creation:
codexresource processes and 73node_replprocesses were live beforemanual cleanup.
node_repl, Computer Use client, andComputer Use service processes.
notification-helper fanout.
However, the original executable identity of each zombie is unavailable.
Therefore the public report should say these helper families are plausible
sources or adjacent lifecycle symptoms, not confirmed identities of the
approximately 9,940 zombies.
Separate adjacent failure modes
The following should remain conceptually separate unless further evidence links
them:
launchdwithPPID=1;EMFILE;gitprocess spawn storms caused by watcher/retry loops.They may share ownership and teardown weaknesses, but they consume different OS
resources and require different direct fixes.
Reproduction and monitoring commands
Compact diagnostic snapshot
Run while the problem is present:
Redact the username, host name, home paths, workspace paths, thread IDs, prompt
payloads, and unrelated processes before posting.
One-shot current Codex check
Long-running recurrence monitor
This version refreshes the PID each iteration, so it survives app updates and
restarts:
Safer emergency recovery near the process limit
When pipelines can no longer fork, use shell builtins:
If the process does not exit:
SIGCHLDwas tested in this reproduction and did not recover the zombies.Raising
maxprocwould only postpone exhaustion and is not a real fix.Issue relevance assessment
Directly relevant
#25744: macOS Computer Use/MCP helpers and unreaped zombies
URL: https://github.com/openai/codex/issues/25744
Relevance: Direct and strongest match
Why:
node_repllifecycle context;This reproduction adds:
kern.maxprocperuidexhaustion;forkfailure;SIGCHLDrecovery experiment;Posting recommendation: Post the comprehensive overview here. This should
be the canonical location.
#12491: GUI MCP children not reaped after task completion
URL: https://github.com/openai/codex/issues/12491
Relevance: Direct historical precedent
Why:
25D125is explicitly represented in the issue;Important difference:
original reported bug was fixed;
Posting recommendation: Do not add the full report to this closed issue.
Link to it from the #25744 overview as prior evidence that a similar failure
class existed and may have regressed or remained in another lifecycle path.
Tangentially relevant
#26293:
SkyComputerUseClient turn-endedhelpers remain as PPID=1 orphansURL: https://github.com/openai/codex/issues/26293
Relevance: Adjacent and potentially related, but different observed state
Similarity:
Difference:
SkyComputerUseClient turn-endedprocessesorphaned to
launchdwithPPID=1;<defunct>children still ownedby the live main Codex GUI process;
argv detail to prove they were
turn-endedhelpers or the zombie source.Posting recommendation: Do not duplicate the full report. Add a short
cross-link only if a fresh process capture independently confirms lingering
SkyComputerUseClient turn-ended ...processes. Otherwise, mention #26293 inthe canonical #25744 report as an adjacent lifecycle path.
#26327: Windows helper-stack accumulation
URL: https://github.com/openai/codex/issues/26327
Relevance: Cross-platform lifecycle analogy
Similarity:
node_repl, MCP, and nested stdio app-server stacks;Difference:
precise enough.
Posting recommendation: Do not post there. A brief mention in #25744 is
enough if cross-platform context is useful.
#26984: MCP stdio pipe-FD and orphan leakage
URL: https://github.com/openai/codex/issues/26984
Relevance: Related ownership/teardown class, different exhausted resource
Similarity:
Difference:
EMFILE/ file-descriptor exhaustion;npm/npxdescendants;EAGAIN-style process-table exhaustion caused byzombies under the Desktop GUI process;
Posting recommendation: Do not post the overview there. Link it from
#25744 only as a possible shared lifecycle class. Comment on #26984 only if a
future reproduction also captures monotonically increasing Codex-owned pipe
FDs with
lsof.Likely not relevant
#26904: WSL slow-filesystem git spawn storm
URL: https://github.com/openai/codex/issues/26904
Relevance: Likely unrelated root cause
Why:
/mntdrvfs trigger;gitprocesses spawned faster than they finish;ENOSPCand retry/backoff behavior;The only commonality is the eventual exhaustion caused by unbounded process
lifecycle behavior.
See Also
I have now identified one concrete source of Codex-owned zombie bursts and filed the implementation-specific report as #27570:
In a prospective process trace, opening/switching older threads caused the main Codex GUI process to spawn exactly 5,042 direct:
children. The zombie count reached exactly 5,042 before the children were eventually reaped.
Inspection of the installed Desktop bundle identified an unbounded fallback in the
review-summaryGit worker:git hash-objectcall.fatal: Unable to hash (null).Promise.all.This provides a confirmed Git-based source for transient main-GUI-owned zombies. It may explain some or all of the approximately 9,940 long-lived zombies previously documented here, but that historical connection remains unproven because their executable identities were already lost.
Adding a current verification snapshot from another macOS Codex.app session (2026-06-22 JST).
Environment:
com.openai.codexcodex-cli 0.142.0-alpha.6codex-cli 0.141.0~/.codex/app-server-control/app-server-control.sock, parent process/Applications/Codex.app/Contents/Resources/codex app-server --analytics-default-enabledcomputer-use,event-stream, andnode_replenabled;onepassworddisabledCurrent observation, without restarting or killing Codex:
SkyComputerUseClient event-stream mcp: 19 processes, oldest sampled process about 16h oldSkyComputerUseClient mcp: 19 processes, oldest sampled process about 16h oldnode_repl: 21 processes, oldest sampled process about 16h oldnode_replreset and JS call currently succeeds, so this is not the immediatesandboxPolicymissing-field failure mode.Health checks:
codex doctor --jsoninitially reported the app-server socket as stale/unreachable with a 2s probe timeout.codex app-server daemon version, a second doctor run reportedapp_server.status=ok.overallStatus=failonly because this noninteractive environment hasTERM=dumb.Repro / verification commands used:
Interpretation:
This looks like the same helper lifecycle / fan-out class as this issue, not a new distinct issue. A useful fix invariant would be that old per-session Computer Use /
node_replhelper families stay bounded or are reaped while the long-lived app-server remains alive.Adding a fresh sanitized macOS Codex.app crash/stackshot data point that looks like the same lifecycle/resource-leak class, but with a more severe end state:
WindowServerwas terminated by watchdog after a Codex-ownednodeprocess storm.Environment:
26.616.81150, bundle build4306Darwin 25.5.0 arm64 arm26.5.1build25F80What happened:
last rebootshowed the machine did not actually reboot; uptime was still about 22 days.WindowServerstackshot / bug type409.WATCHDOG, indicatormonitoring timed out for service.Most relevant stackshot numbers, redacted and aggregated:
Largest processes in the Codex coalition:
This seems important because the biggest signal was not only Codex renderer memory. The stackshot had more than a thousand
nodechildren/related processes grouped into the same Codex jetsam coalition. That matches the helper-process accumulation pattern reported above, but the observed impact reached a system UI watchdog termination.Other context:
Expected behavior:
node,node_repl, or app-server helper processes across a long-running desktop session.WindowServerwatchdog reset.Useful local diagnostic commands for future reports:
I intentionally omitted raw paths, usernames, project/workspace names, third-party project identifiers, display/peripheral names, and the raw stackshot process inventory.
Adding a current helper-accumulation datapoint from the latest macOS Desktop build I have installed.
Snapshot time: 2026-06-28 11:26 JST
Environment:
26.623.42026, build45140.142.3Current sanitized helper counts:
Selected app-server FD counts from the same snapshot:
This looks like the same lifecycle class as this issue, with one extra angle: in a multi-surface setup where Codex Desktop, CLI, VS Code, and Cursor are all present, the shared MCP/tool configuration appears to multiply the same helper families across surfaces. I did not kill or restart anything before collecting these numbers.
Omitting raw process args, full local paths, and private project/tool details here.
Adding a narrower source-level repro for the app-server/MCP helper retention path. This is from a current local
openai/codexcheckout, with local paths sanitized.The live symptom was not just macOS FD pressure. Once the long-running app-server accumulated repeated MCP/helper runtime groups, new Codex unified exec process creation started failing with:
At the same time, an already-live Node MCP runtime could still spawn
/bin/pwd, so the failure was not a global OS inability to fork/exec. It pointed at Codex session/tool runtime lifecycle and unified exec descriptor handling under retained helper pressure.Concrete repro test
I added an ignored regression test in:
Test name:
The test does this:
MCP_TEST_PID_FILE.synccall that holds the current MCP runtime.fixture.codex.shutdown_and_wait().await.Run command:
Observed failure:
Without
RUST_MIN_STACK=16777216, this same scenario can abort earlier with a Tokio worker stack overflow during shutdown, so the larger stack is useful to reach the cleaner process-retention assertion.Repro patch
<details>
<summary>Patch for the ignored repro test</summary>
</details>
Suspected root cause path
shutdown_session_runtime()currently shuts down:This repro shows that a superseded runtime can remain alive when an in-flight tool call still holds it. The latest runtime is shut down, but the old runtime held by the in-flight call is not reliably cancelled/shut down by session shutdown.
That gives a concrete source for stale stdio MCP/helper processes surviving after the owning session/thread is closed or shut down. In a long-running Desktop/app-server process, repeated occurrences can leave old helper groups and their descriptors around until app-server eventually hits the soft FD boundary and unified exec starts failing.
Useful fix invariant
Session shutdown / thread close should cancel and shut down all MCP runtime snapshots associated with that session, not only the latest published runtime. Superseded runtimes held by in-flight tool calls need an owner-session shutdown/cancellation path so their stdio children cannot survive the session that created them.
Posting the repro test source directly, since the previous comment had it inside a collapsed
<details>diff block.File:
Source to append after
refresh_keeps_superseded_mcp_server_alive_for_in_flight_calls:Run command:
Observed failure: