Skip to content

MCP App webviews leak Codex Renderer processes on thread reopen (203 widgets → +209/+216 renderers) #32663

Open

Description

@hyunn515

Summary

Codex Desktop appears to create an Electron renderer/webview for each hydrated MCP App widget in a thread, and those renderer processes are not reclaimed when the user switches away and reopens the thread.

In one reproducible local thread:

  • The rollout contained 203 Atlassian MCP calls with an MCP App resource URI.
  • Opening the thread created renderer batches of 161 + 48 = 209 processes.
  • Reopening the same thread later created another 161 + 55 = 216 renderer processes.
  • The previous renderer processes remained alive.
  • A previous app run accumulated more than 1,600 Codex (Renderer) processes.
  • After a fresh app start, 548 renderer processes had accumulated in 1h 41m.

The created processes are direct children of the Codex desktop main process and run as:

Codex (Renderer) --type=renderer

This appears distinct from the stdio MCP server child-process leak. The leaking processes here are Electron/Chromium renderers associated with MCP App webviews.

Environment

  • Codex Desktop: 26.707.51957 (build 5175)
  • Bundle ID: com.openai.codex
  • Bundled CLI: codex-cli 0.144.0-alpha.4
  • macOS: 26.5 (25F71)
  • Architecture: Apple Silicon, arm64

Thread characteristics

  • Rollout size: 53,784,301 bytes
  • JSONL lines: 2,736
  • MCP tool-call completion events: 320
  • MCP calls with Atlassian widget resource URIs: 203
  • MCP calls in the most recent five turns: 287
  • Largest recent turn: 328 items
  • Several individual persisted events were 500 KB to 1.1 MB

The 328-item turn is below the desktop client's per-turn item hydration limit, so it is loaded in full.

Reproduction

  1. Create or open a conversation whose recent turns contain approximately 200 completed MCP App calls with mcpAppResourceUri values.
  2. Switch to the conversation in Codex Desktop.
  3. Count direct child renderer processes of the Codex main process.
  4. Switch to another conversation.
  5. Reopen the widget-heavy conversation.
  6. Count the renderer processes again.

Observed renderer creation batches:

First open:   161 + 48 = 209
Second open:  161 + 55 = 216

The earlier processes remained alive. The number created per open closely matches the 203 MCP App widgets in the thread.

All 548 renderer children in the later sample were still in the sleeping state. Aggregate RSS is not reported here because Chromium processes share memory, but the busiest renderer alone was approximately 1.5 GB RSS and 28% CPU in that sample.

Desktop bundle observations

Inspection of the installed app.asar shows that the desktop client:

  1. Resumes with the most recent five turns requested as itemsView: "full".
  2. On the durable-history path, requests turns as notLoaded and then eagerly calls thread/items/list for each recent turn.
  3. Hydrates up to 500 items per turn with five turns processed concurrently.
  4. Retains the full MCP invocation arguments, result, and mcpAppResourceUri in renderer state.
  5. Uses document.createElement("webview") in the MCP App sandbox rendering path.

This means visually collapsed tool cards can still carry the full payload and instantiate widget infrastructure.

Related public source path

The app-server supports Summary and NotLoaded turn views, but the desktop client requests full recent turns. The current app-server implementation also notes that thread/turns/list still replays the entire rollout on every request until turn metadata is indexed separately:

.map_err(thread_read_view_error)?;
// This API optimizes network transfer by letting clients page through a
// thread's turns incrementally, but it still replays the entire rollout on
// every request. Rollback and compaction events can change earlier turns, so
// the server has to rebuild the full turn list until turn metadata is indexed
// separately.
let loaded_thread = self.thread_manager.get_thread(thread_uuid).await.ok();

PR #30188 adds groundwork for paginated histories and later SQLite materialization, but this thread is a legacy rollout and the renderer/webview lifecycle issue remains observable in the current desktop build.

Expected behavior

  • Collapsed or offscreen MCP tool cards should not instantiate a webview.
  • Only an explicitly expanded, visible MCP App should have a live webview.
  • Switching away from a thread should destroy its MCP App webviews and reclaim renderer processes.
  • Renderer process count should return close to baseline after closing or switching away from a widget-heavy thread.
  • Large result payloads should be fetched on demand rather than eagerly copied into renderer state.
  • Reopening the same conversation repeatedly should not increase process count.

Suggested regression test

  1. Build a fixture containing 200 completed MCP App items in one recent turn.
  2. Open and close the thread ten times.
  3. Assert that live MCP webviews remain bounded, ideally one active webview.
  4. Assert that renderer process count returns to baseline after unmount.
  5. Repeat with 50 MB and 250 MB legacy rollout fixtures.
  6. Track hydrated bytes, live webview count, destroyed webview count, and resume latency.

Related issues

Those reports cover long-thread and renderer slowdown symptoms. This report adds a repeatable near one-to-one correlation between MCP App widget count and newly leaked renderer processes on every thread reopen.

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    mcpIssues related to the use of model context protocol (MCP) servers
    on Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    appIssues related to the Codex desktop appbugSomething isn't workingmcpIssues related to the use of model context protocol (MCP) serversperformance

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions