[macOS App] Restored tasks fan out plugin MCP servers (~30x) and crash/reload the renderer

Open 💬 7 comments Opened Jul 14, 2026 by rdylina
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of the Codex App are you using (From “About Codex” dialog)?

26.707.71524 (build 5263)

What subscription do you have?

ChatGPT Pro

What platform is your computer?

Darwin 25.5.0 arm64 arm (macOS 26.5.1, Apple silicon, 64 GiB RAM)

What issue are you seeing?

Status correction, July 13: The Xcode-specific workaround described in the original report did not fix the issue. Another renderer crash occurred after that gate was installed. This issue remains open and reproducible, and the evidence now shows a broader restored-task plugin/MCP lifecycle defect.

Codex Desktop appears to create a full set of plugin MCP/tool helper processes for every restored task context. Those helpers accumulate under the long-lived app-server instead of being shared, started lazily, or reaped at a bounded task lifecycle. The Chromium renderer then crashes and is recreated while the outer application and app-server remain alive. The visible result is that the window blanks, disappears, and reloads as if freshly opened.

At the latest live snapshot, approximately 30 restored task contexts produced 30 copies of each helper family:

| Helper family | Copies | Combined RSS |
| --- | ---: | ---: |
| codex-security MCP | 30 | 1.81 GiB |
| Data Analytics widgets MCP | 30 | 1.35 GiB |
| Sites design-picker MCP | 30 | 1.23 GiB |
| OpenAI API-key confirmation MCP | 30 | 1.21 GiB |
| node_repl | 30 | 0.43 GiB |

The app-server used about 3.5-3.9 GiB RSS. Its 153 direct children used about 6.1 GiB, and the full Codex process tree used about 16.6 GiB.

The latest renderer crash was correlated precisely:

  • Renderer Crashpad sidecar: 30d3568e-b947-4812-8565-ce474defa2fc_sidecar.json
  • Created: 2026-07-13 21:02:45 PDT (2026-07-14 04:02:45 UTC)
  • Sidecar contents: {"osarch":"arm64","ptype":"renderer"}
  • Replacement renderer started at 21:02:46 PDT
  • React-root render requested at 21:02:48.294 PDT
  • Routes remounted at 21:02:48.802 PDT
  • Desktop and app-server PIDs did not change
  • Replacement renderer grew from about 2.21 to 2.30 GiB RSS in ten seconds and continued showing high CPU use

This event was not a system-wide OOM. The machine has 64 GiB RAM, memory_pressure reported about 89% available, and swap usage was zero. The failure instead coincided with a large per-restored-task helper baseline plus an actively growing renderer and app-server.

Xcode was one leaked family, not the complete defect

The initial investigation found many restored tasks launching:

npm exec xcodebuildmcp@latest mcp

That command continued to launch despite all relevant controls resolving disabled:

[mcp_servers.xcode]
command = "xcrun"
args = ["mcpbridge"]
enabled = false

[plugins."build-ios-apps@openai-curated-remote"]
enabled = false

[plugins."build-ios-apps@openai-curated-remote".mcp_servers.xcodebuildmcp]
enabled = false

[features]
remote_plugin = false

The similarly named local marketplace plugin, build-ios-apps@openai-curated, was also removed. Verified clean starts still launched 13 and then 11 XcodeBuildMCP npm processes for restored tasks.

An initial $HOME/.local/bin/npx gate appeared to contain that exact command in short delayed samples. It was not a fix:

  1. A nested codex exec inherited a PATH with Homebrew before $HOME/.local/bin, bypassing the gate.
  2. Another renderer crash occurred 21 minutes after the gate was installed.
  3. At that crash, only one XcodeBuildMCP launcher/worker pair remained (about 142 MiB) and no new mcpbridge crash coincided with the renderer failure.
  4. The dominant load was the 30x fan-out of four other plugin MCP servers plus Node REPL.

Updating Xcode did not fix the behavior. It reproduced on Xcode 26.5 and continued after upgrading to Xcode 26.6 (17F113).

What steps can reproduce the bug?

  1. Use Codex Desktop with multiple tasks restorable across several workspaces and several MCP-bearing plugins available.
  2. Fully quit and reopen Codex so those task contexts restore.
  3. Inspect the app-server's child process tree and each generic Node server's working directory.
  4. Observe one copy per restored task of plugin servers such as Codex Security, Data Analytics widgets, Sites design picker, OpenAI API-key confirmation, and Node REPL.
  5. Continue ordinary work while the helper population and renderer/app-server memory grow.
  6. Observe the renderer blank/reload event without a clean desktop or app-server exit.

The Xcode policy-bypass variant is independently reproducible:

  1. Make build-ios-apps@openai-curated-remote available to restored tasks.
  2. Disable the exact remote plugin and its xcodebuildmcp server, set remote_plugin = false, and remove the local build-ios-apps@openai-curated copy.
  3. Fully quit Codex, verify new desktop/app-server PIDs after reopening, and wait several minutes.
  4. Run:

``text
pgrep -af 'xcodebuildmcp@latest mcp'
``

Actual result: restored tasks can still launch XcodeBuildMCP even though the current plugin registry, install state, exact plugin/MCP overrides, and remote-plugin feature all resolve disabled.

What is the expected behavior?

  • Restored tasks must re-resolve current plugin and MCP policy before starting helpers.
  • A plugin, app, or MCP with enabled = false must never launch from persisted task state.
  • MCP servers should start lazily only for tasks that need them.
  • Identical safe servers should be shared where appropriate, or otherwise have a bounded per-task lifecycle.
  • Closing, archiving, restoring, interrupting, reloading, or shutting down a task must reliably reap its subprocess tree.
  • Renderer reconstruction must not replay stale tool state in a way that multiplies helpers again.
  • The desktop should warn or trip a circuit breaker before process or memory growth crashes the renderer.
  • Diagnostics should identify each MCP process's owning task, plugin identity/source, configuration layer, uptime, RSS, and restart count.

Additional information

Current local posture — tool catalog restored; Xcode-only containment remains

The broad plugin/app circuit breaker was rolled back because it disabled core functionality that Codex documents as part of its intended parallel, tool-rich workflow—and it did not prevent another renderer failure.

At 2026-07-13 22:08:59 PDT, Crashpad wrote another renderer sidecar, fb55976b-b3bb-486b-8e9f-a331a6ff30fd_sidecar.json, while plugins = false and apps = false were still configured. The app had already rewritten node_repl to enabled = true, and 12 Node REPL processes were present. The outer desktop and app-server remained the same long-lived processes.

At approximately 22:12 PDT, all non-Xcode tool layers were restored:

[features]
plugins = true
apps = true
remote_plugin = true

[mcp_servers.node_repl]
enabled = true

The Codex Security, Data Analytics, Sites, and OpenAI developer plugin MCP identities also resolve enabled again. The supported CLI now reports plugins, apps, remote plugins, Node REPL, and those four plugin MCPs enabled.

Only the known Xcode-specific launch paths remain contained:

  • native xcrun mcpbridge: disabled
  • build-ios-apps@openai-curated-remote: disabled
  • its xcodebuildmcp MCP: disabled
  • exact npx -y xcodebuildmcp@latest mcp command: routed through the project allow-list from both observed PATH positions

The still-running pre-change backend began loading the restored tool set immediately. A live sample showed 3 Codex Security MCPs, 6 Data Analytics MCPs, 7 Sites MCPs, 8 OpenAI developer MCPs, 12 Node REPLs, and zero XcodeBuildMCP processes. The backend used about 4.3 GiB RSS; its 44 direct children used about 1.8 GiB. This is not a clean-start capacity measurement.

The earlier broad disablement is retained only as historical diagnostic evidence. It is not the current configuration and did not fix the renderer crash. The upstream restored-task lifecycle and policy-enforcement defect remains unresolved.

Earlier observed impact
  • 25 renderer Crashpad sidecars on July 13 before the latest recurrence
  • 38 mcpbridge diagnostic reports
  • 27 simultaneous XcodeBuildMCP npm launchers using about 1.9 GiB RSS in one snapshot
  • 55 XcodeBuildMCP node workers using about 4.1 GiB RSS in an earlier snapshot
  • Earlier backend peak around 4.1 GiB with hundreds of direct children and about 12.7 GiB swap in use
Related issues
  • #17574 — subagents leak XcodeBuildMCP and Chrome DevTools MCP trees
  • #28443 — remote curated plugins ignore per-plugin enabled = false
  • #31946 — macOS Node process explosion and system-wide memory failure
  • #27880 — renderer/browser crashes with stale app-server helpers
  • #32339 and #32797 — flagged by the duplicate-detection action as potentially related

Project names, prompts, and private local paths are intentionally omitted. Sanitized process samples, configuration snapshots, and crash metadata can be provided if needed.

View original on GitHub ↗

7 Comments

github-actions[bot] contributor · 6 days ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #32339
  • #31946
  • #32797

Powered by Codex Action

rdylina · 6 days ago

Follow-up: another renderer crash proves this is broader than XcodeBuildMCP

Another renderer-only crash/reload occurred on July 13 at 21:02:45 PDT (04:02:45 UTC), 21 minutes after the initial PATH-level Xcode gate was installed.

Correlation for this event:

  • Crashpad created renderer sidecar 30d3568e-b947-4812-8565-ce474defa2fc_sidecar.json at 21:02:45 PDT with {"osarch":"arm64","ptype":"renderer"}.
  • A replacement renderer started at 21:02:46.
  • The desktop log requested a new React-root render at 21:02:48.294 and reported routes mounted at 21:02:48.802.
  • The outer desktop process and app-server did not exit; both retained their original start time.
  • The replacement renderer reached about 2.3 GiB RSS and was still growing, including a measured 2.21 -> 2.30 GiB increase in ten seconds.

The dominant live process load was not Xcode this time. The app-server had approximately 30 restored task contexts, each with one copy of every MCP-bearing plugin/tool:

| Restored-task helper | Copies | Combined RSS |
| --- | ---: | ---: |
| codex-security MCP | 30 | 1.81 GiB |
| Data Analytics widgets MCP | 30 | 1.35 GiB |
| Sites design-picker MCP | 30 | 1.23 GiB |
| OpenAI API-key confirmation MCP | 30 | 1.21 GiB |
| node_repl | 30 | 0.43 GiB |

The app-server itself used about 3.5-3.7 GiB RSS, and its 153 direct children used about 6.1 GiB. The full Codex process tree used about 16.6 GiB.

This was not a system-wide OOM: the machine has 64 GiB RAM, memory_pressure reported about 89% available, and swap usage was zero. Instead, Codex had a large per-restored-task helper baseline plus an actively growing renderer/app-server.

Only one residual XcodeBuildMCP launcher/worker pair remained (about 142 MiB), and no new mcpbridge crash coincided with this renderer crash. That pair also exposed a gap in the initial wrapper: a nested codex exec inherited a PATH with Homebrew before $HOME/.local/bin, so it bypassed the wrapper.

The local emergency circuit breaker is now broader and deliberately reversible:

[features]
plugins = false
apps = false
remote_plugin = false

[mcp_servers.node_repl]
enabled = false

The four observed plugin MCP identities are also explicitly disabled, and the exact Xcode wrapper is now present in the earlier $HOME/.npm-global/bin PATH location as well. After terminating 120 plugin MCPs, 30 Node REPLs, and the remaining Xcode pair, all targeted helper counts remained zero in a delayed sample.

This strengthens the root-cause hypothesis: restored tasks instantiate a full plugin/MCP set per task and retain those processes, while current plugin controls are not reliably re-resolved for restored/nested contexts. XcodeBuildMCP was the first conspicuous family, not the complete defect.

The durable upstream fix should bound this lifecycle: re-resolve current policy on restore, start MCPs lazily, share safe servers where appropriate, reap each task's subprocess tree, and prevent renderer reconstruction from replaying all stale task/tool state at once.

rdylina · 6 days ago

Correction: I have updated the primary issue body because the original “effective local workaround” claim was wrong.

The $HOME/.local/bin/npx Xcode gate was bypassed by nested Codex PATH ordering, and another renderer crash occurred afterward. The issue is not fixed. The latest evidence shows approximately 30 restored task contexts each launching a complete set of four plugin MCP servers plus Node REPL.

The broad plugins = false, apps = false, and node_repl.enabled = false configuration is only a temporary diagnostic circuit breaker. It has not yet been verified across a clean app-server restart and does not resolve the upstream lifecycle defect.

rdylina · 6 days ago

Update: tool catalog restored; broad circuit breaker did not prevent another crash

The temporary plugins = false / apps = false diagnostic circuit breaker has been rolled back. Disabling the tool catalog removed functionality Codex is designed to support and did not prevent another renderer failure.

Crashpad wrote renderer sidecar fb55976b-b3bb-486b-8e9f-a331a6ff30fd_sidecar.json at 2026-07-13 22:08:59 PDT, while plugins = false and apps = false were still configured. The app had already rewritten Node REPL to enabled, with 12 REPL processes present. The desktop and app-server remained the same long-lived processes.

At approximately 22:12 PDT, plugins, apps, remote plugins, Node REPL, and the four observed non-Xcode plugin MCP identities were restored. Only the native Xcode bridge and remote build-ios-apps XcodeBuildMCP path remain disabled/gated.

The still-running backend immediately began loading the restored tools. A post-change sample found 3 Codex Security MCPs, 6 Data Analytics MCPs, 7 Sites MCPs, 8 OpenAI developer MCPs, 12 Node REPLs, and zero XcodeBuildMCP processes. The backend was about 4.3 GiB RSS with 44 direct children using about 1.8 GiB.

This is not a claim of resolution. It records that the broad disablement was both functionally unacceptable and insufficient to stop renderer crashes. A clean restart is still needed for a fresh process baseline, while the upstream lifecycle defect remains open.

alimohajer · 6 days ago

Windows corroboration and a small bounded reproduction on OpenAI.Codex_26.707.9564.0 (Windows 11 Pro for Workstations 10.0.26200, 16 logical CPUs, 64 GB RAM).

After one root task and four completed subagents, a single long-lived codex app-server had these direct children:

  • 5 x node ./mcp/server.cjs --stdio
  • 5 x node ./mcp/server.mjs
  • 6 x node_repl.exe

The five MCP pairs started in clusters that matched the root/subagent launches. All were idle in a 12-second CPU sample and had no TCP connections, but the completed contexts' helpers remained resident.

I retained the oldest root-task MCP pair and REPL, then terminated the later completed-context helpers. Results:

  • 13 processes stopped
  • approximately 280.8 MB working set released
  • count remained exactly 1 CJS server + 1 MJS server + 1 REPL for 20 seconds
  • no terminated helper respawned during that window

This supports the broader task/subagent lifecycle finding on Windows: completed contexts retain their plugin/MCP and REPL helpers under the app-server. It is not an OS-orphan case because the app-server remains the parent. Project names, local paths, prompts, and logs are omitted.

rdylina · 4 days ago

Deep-dive update: five reproducible renderer-only failures; Xcode is not the trigger

I ran a new read-only investigation on the current macOS build and inspected the packaged Electron/Owl code. This corrects the earlier workaround narrative and separates the verified failure boundary from the still-unobserved native exit reason.

Environment:

  • ChatGPT/Codex macOS app 26.707.91948 (build 5440)
  • bundled codex-cli 0.144.5
  • Chromium/Owl framework 150.0.7871.115
  • macOS 26.5.1 arm64, 64 GB RAM
  • Xcode is now 26.6 (17F113)
Reproduced sequence

Five new Crashpad sidecars in one desktop lifetime contained exactly:

{"osarch":"arm64","ptype":"renderer"}

They arrived at 23:49:24, 23:56:12, 00:03:10, 00:09:39, and 00:17:27 PDT. The four recurrence intervals were 408, 418, 389, and 468 seconds (mean about 7m01s). Each was followed 1.3–2.1 seconds later by a new primary React-root render.

The same outer desktop process, GPU process, and primary codex app-server survived all five events. This is therefore not a whole-app restart and not a GPU-process restart. It is a primary renderer reported gone followed by automatic recovery.

Renderer memory was severe but the final exit reason is still missing:

  • one replacement renderer grew from about 1.64 GiB RSS in under one minute to 3.17 GiB before disappearing;
  • a live vmmap measured 3.0 GiB physical / 3.6 GiB peak, dominated by Chromium/V8 PartitionAlloc memory tags;
  • another replacement reached about 1.67 GiB within roughly one minute;
  • the surviving app-server remained roughly 3.8–4.4 GiB RSS and 127–204% CPU.

This proves rapid multi-gigabyte renderer growth and a repeatable renderer-only failure loop. It does not prove OOM: current Crashpad retained no .dmp, macOS produced no matching DiagnosticReport, and the plaintext app log does not retain Electron's render-process-gone reason or exit code.

Packaged-code findings

Read-only inspection of the exact installed ASAR verifies:

  1. render-process-gone schedules webContents.reload() after 500 ms.
  2. The local stdio app-server is owned by the persistent desktop main process and is not restarted by that recovery handler.
  3. Normal app-server notifications are broadcast to every non-destroyed registered window.
  4. A rebuilt renderer starts with an empty conversation store and asynchronously reconstructs state with thread/list / thread/read work.
  5. Window registration has no generic renderer epoch, notification sequence acknowledgment, or ready/replay barrier.
  6. The familiar unknown conversation / Conversation state not found paths are guarded drops/returns, not demonstrated fatal exceptions.
  7. Hydration already has some memoization/deduplication, so this is not a claim that every history request is naively duplicated.

In the live log, cross-task unknown conversation traffic jumped from roughly 0–2/minute to about 273/minute immediately after several subagents started, before the first failure. After every reload, the app also attempted to resume the same 13 unarchived legacy tasks. Their rollout files total 1.751 GiB on disk; that is only a recovery-scope proxy, not a claim that all 1.751 GiB was decoded into renderer memory.

The strongest current working hypothesis is severe renderer memory/event pressure, with global cross-task fanout, broad recovery reconstruction, and retained backend/tool-host state as contributors. Their individual causal contribution and the renderer's final native exit mechanism still require controlled instrumentation.

Xcode and the previous cleanup
  • The observed XcodeBuildMCP process started after the first three failures. There was no correlated Xcode MCP error, and the renderer cycle continued after Xcode reached 26.6. Xcode is not the trigger for this observed sequence.
  • The guarded chat_processes.json repair previously described never ran. It timed out waiting for a “full quit” condition that also matched old orphan helpers/Crashpad handlers, created no backup, and made no cleanup. It cannot be credited as either a fix or a failed fix.
  • The registry does contain 166 persistent stale terminal fragments, but packaged code shows that registry is observational and does not spawn processes. Its causal role is unproved and should be tested separately.
Requested vendor action

Please instrument and harden the renderer/app-server recovery contract:

  1. Durably log render-process-gone reason, exit code, renderer PID, Crashpad report ID, JS/native heap, queue depth, payload bytes, and target/owner identity.
  2. Route task events only to subscribed/owning renderer surfaces; measure and prevent delivery to unloaded conversations.
  3. Add renderer epochs and ordered sequence checkpoints; dispose renderer-owned subscriptions/listeners/pending requests on death.
  4. Buffer/coalesce bounded deltas until an explicit renderer-ready-and-hydrated acknowledgment.
  5. Restore foreground state first, paginate/cancel heavy history work, and add byte/item bounds and backpressure.
  6. Reap idle MCP/Node REPL/code-mode/nested app-server hosts at task/session end and parent disconnect.
  7. Add a crash-recovery circuit breaker instead of blindly recreating the same workload.

Related reports now independently cover the same families: #23725 (macOS renderer reset + unknown-conversation state skew), #32339 / #31946 / #32797 (cross-platform retained MCP/Node process pools), and #33521 (the same 26.707.91948 UI package crashes a Linux VS Code renderer while rollback to 26.707.71524 is stable). PR #19753 added MCP cleanup on explicit session/manager shutdown, but the current long-lived desktop lifecycle still retains hosts, suggesting the relevant shutdown boundary is not reached or another ownership path remains.

I can provide sanitized timelines, exact packaged-code offsets, process samples, and the vmmap summary privately. Raw logs and task files are not attached because they contain private task metadata.

rdylina · 4 days ago

Update: crashes continue; paginated history is not in the current desktop/CLI release

The renderer failure loop is still active on the same macOS desktop build (26.707.91948, build 5440; packaged codex-cli 0.144.5). No newer desktop build is currently being offered to this machine.

Continued failures

Since the previous deep-dive comment's 00:17 PDT cutoff, Crashpad recorded 17 additional ptype: renderer sidecars: four more through 00:48, then 13 more from 09:41 through 15:42:50 PDT. That brings the extended observation sequence to 22 renderer sidecars. I have not individually correlated every later sidecar with a primary-window remount, so this count establishes continued renderer failures rather than claiming that every artifact has a fully reconstructed UI timeline.

The strongest fully correlated sample remains unchanged:

  • the outer desktop, GPU process, and app-server survived while the primary renderer was replaced;
  • a replacement primary renderer reached about 2.88 GB working set / 3.93 GB peak;
  • the live renderer store contained 53 tasks (14 active), 9 inflight turns, 946 turns, and 23,230 items;
  • one captured desktop lifetime issued 6,354 experimental thread/turns/list requests and logged 14,522 guarded unknown conversation / Conversation state not found events involving 19 conversation IDs.

These observations still do not reveal the native exit reason, but they show a persistent renderer-only failure loop under multi-gigabyte renderer growth and broad cross-task recovery activity.

Every local task still uses legacy history

The latest read-only SQLite snapshot contains 7,257 indexed tasks and every row has history_mode = 'legacy'; there are zero paginated tasks. Inspection of the installed desktop client also found no caller that requests thread/start.historyMode = "paginated", so newly created desktop tasks continue to default to legacy history.

This matters because the current legacy thread/turns/list path still has to replay/read legacy rollout history while the renderer reconstructs many tasks. A paginated API call against legacy storage is not the same thing as paginated thread storage.

Feature-gate finding

The desktop host sends process-wide experimentalFeature/enablement/set overlays after app-server startup. Its current overlay enables concurrent_reasoning_summaries and enable_mcp_apps even though the packaged registry classifies both as under-development and default-off in a normal CLI/app-server launch. It also enables app-only surfaces including writing_blocks and thread_tools.

concurrent_reasoning_summaries first appeared in local desktop overlays on July 9 with the 0.144.0-alpha.4 app-server. This is a hypothesis, not proven causality: concurrent summary fanout may amplify renderer event volume and recovery pressure. Because the desktop's host overlay can supersede config.toml, it is not a reliable local toggle test.

Potentially protective features are already active: remote compaction, request compression, image resizing, and deferred MCP tool loading. Conversely, local_thread_store_compression, runtime_metrics, and multi_agent_v2 are under-development and are not safe or plausible renderer fixes. I have not edited SQLite history modes or enabled those flags.

The latest stable standalone CLI does not contain the paginated-history work

The latest public stable CLI release is 0.144.5 (rust-v0.144.5, tag commit 87db9bc18ba5bc82c1cb4e4381b44f693ee35623). The desktop already bundles that exact version. During this investigation, the active standalone PATH CLI was updated from 0.144.1 to 0.144.5 and verified. The desktop still explicitly launches /Applications/ChatGPT.app/Contents/Resources/codex; updating the standalone installation did not replace the desktop app-server and cannot fix its renderer.

Two merged main changes directly address the missing bounded-history path:

  • #32896 — load model context from a bounded rollout suffix
  • #33364 — enable paginated thread history in app-server, including bounded resume and cursor-based hydration

Neither merge commit is an ancestor of the rust-v0.144.5 release tag; the release branch diverged before both changes. The 0.144.5 release itself contains a dangerous-command-detection fix, not a desktop renderer or history-recovery fix.

Requests to maintainers
  1. Please identify the first desktop build that includes #32896 and #33364, and confirm that its desktop client actually creates paginated tasks.
  2. Please clarify the migration or bounded-recovery plan for existing legacy tasks. Paginated storage for new tasks alone will not fix 7,257 existing legacy records.
  3. Please run a one-factor cohort test for this account with concurrent_reasoning_summaries disabled. If there is no improvement, test enable_mcp_apps separately rather than changing both at once.
  4. Please retain the earlier instrumentation request: durable render-process-gone reason/exit code/Crashpad ID, renderer heap and payload/queue measurements, renderer epochs, subscription cleanup, bounded hydration/backpressure, and a recovery circuit breaker.
  5. Please confirm whether any supported desktop setting can select an external app-server binary. If not, a standalone CLI update should not be recommended as a desktop-crash workaround.

Raw task histories and logs remain private, but sanitized gate lists, counts, schema output, timelines, and process samples can be provided.