-
Notifications
You must be signed in to change notification settings - Fork 9.4k
MCP sub-agent processes are never terminated when codex sessions are stopped/suspended #16256
Description
What version of Codex CLI is running?
codex-cli 0.117.0
What subscription do you have?
ChatGPT Pro
Which model were you using?
gpt-5.4
What platform is your computer?
Linux 6.14.2-arch1-2 x86_64, GNOME 48, 16GB RAM + 8GB zram
What terminal emulator and version are you using (if applicable)?
GNOME Terminal (VTE-based), multiple pts sessions
What issue are you seeing?
When a Codex CLI session is stopped (Ctrl+Z) or suspended, the MCP server sub-processes it spawned (e.g. context7, claude-agent-mcp, infra, ai-dx-mcp, image-creator, ai3d, and others) are not terminated. Starting a new codex session on the same terminal (pts) layers a completely new set of MCP processes on top of the old ones.
Over a typical work session this leads to massive process accumulation:
- 16 codex sessions found running simultaneously, many in
Tl(stopped) state -
- The most active single session held 291 child processes, including dozens of duplicate MCP server instances
-
-
- Multiple codex processes stacked on the same tty:
-
-
-
-
pts/1: 2 codex processes (one stopped, one active)
-
-
-
-
-
-
pts/2: 3 codex processes (two stopped, one active)
-
-
-
-
-
-
-
-
pts/3: 3 codex processes (two stopped, one active)
-
-
-
-
-
-
-
-
-
-
pts/4: 3 codex processes (two stopped, one active)
-
-
-
-
-
-
-
-
-
-
-
-
- A headless Chromium instance spawned by a browser MCP server on
127.0.0.1:9222had accumulated 571 chrome child processes, 1151 DevTools targets (994 of which wereabout:blank), and 566 renderer processes
- A headless Chromium instance spawned by a browser MCP server on
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- zram swap was completely saturated: 8/8 GiB
-
-
-
-
-
-
-
Root cause
Each codex CLI session spawns its own independent tree of MCP server processes. When the session is suspended (e.g. via Ctrl+Z or by starting a new session in the same terminal), the MCP child process tree is not cleaned up. The stopped parent codex process continues to hold references to all its children, but they remain alive and consuming memory.
There is no teardown/cleanup hook that fires when a codex CLI session transitions to a stopped state, and no mechanism to share or deduplicate MCP server instances across sessions.
Impact
- Hundreds of duplicate
node,npx,uvxprocesses consuming RAM -
- System becomes unusable as swap fills up
-
-
- On a 16GB machine with 8GB zram, the system was brought to near-OOM by accumulated MCP processes alone
-
Related issues
- Codex.app GUI: MCP child processes not reaped after task completion — 1300+ zombies, 37GB memory leak #12491 — Same root cause on macOS Codex.app GUI (MCP child processes not reaped after task completion)
-
- Detailed Windows stdio MCP teardown report with ready patch branch #16144 — Windows stdio MCP teardown report
This appears to be a cross-platform problem where MCP process lifecycle is not properly managed. The Linux CLI case is particularly bad because terminal job control (Ctrl+Z) makes it very easy to accidentally stack sessions.
- Detailed Windows stdio MCP teardown report with ready patch branch #16144 — Windows stdio MCP teardown report
What steps can reproduce the bug?
- Configure several MCP servers in
~/.codex/config.toml(e.g.context7,ai-dx-mcp,infra,image-creator,ai3d, or any stdio-based MCP servers) -
- Open a terminal and run
codex
- Open a terminal and run
-
- Wait for MCP servers to initialize (observe child processes via
pstree -p $(pgrep -f codex))
- Wait for MCP servers to initialize (observe child processes via
-
- Suspend the session with
Ctrl+Z
- Suspend the session with
-
- Run
codexagain in the same terminal
- Run
-
- Repeat steps 4-5 several times
-
- Inspect the process tree:
# Count codex sessions
ps aux | grep '[c]odex' | wc -l
# Show process tree for each
for pid in $(pgrep -f 'codex'); do
echo "=== PID $pid ==="
pstree -p "$pid" | head -20
done
# Count total MCP-related processes
ps aux | grep -E 'context7|ai-dx-mcp|infra|image-creator|ai3d|claude-agent-mcp' | wc -lExpected: Old MCP processes should be terminated when the codex session is stopped/suspended, or at least when a new session starts on the same tty.
Actual: Every suspended session retains its full MCP child tree. Process count grows linearly with each new session.
What is the expected behavior?
- When a
codexCLI session is suspended (Ctrl+Z/SIGTSTP), all MCP child processes should be gracefully terminated (SIGTERM → SIGKILL after timeout). -
- When a
codexCLI session exits (normally or via signal), all MCP child processes must be reaped — no orphans should remain.
- When a
-
- Ideally, MCP server instances should be shared/pooled across sessions or at minimum deduplicated per-user, instead of each session spawning its own full copy of every configured MCP server.
-
- Headless browser instances spawned by MCP servers (e.g. Chromium on
:9222) should have lifecycle management — idle tabs should be closed, and the browser should exit when its parent MCP server terminates.
- Headless browser instances spawned by MCP servers (e.g. Chromium on
Additional information
Suggested fix directions
- Register a
SIGTSTPhandler in the codex CLI process that sendsSIGTERMto all MCP child processes before the parent is suspended. -
- Use process groups (
setpgid) so that the entire MCP subtree can be killed with a singlekill(-pgid, SIGTERM).
- Use process groups (
-
- Add a cleanup-on-exit hook that walks the child process tree and ensures no orphans survive.
-
- Consider MCP server pooling — a single long-lived MCP server daemon per user that multiple codex sessions can connect to, instead of each session spawning its own.
Process audit data
A full process audit was captured at the time of observation and saved locally. Key numbers:
- 16 concurrent codex sessions (most in stopped state)
-
- ~1,500 total MCP-related child processes
-
-
- 571 headless Chrome children from a single browser MCP
-
-
-
-
- 8/8 GiB zram completely saturated on a 16 GiB machine
-
-
Cross-references
This is the Linux CLI manifestation of a broader cross-platform issue: