Skip to content

Windows Codex Desktop: opening/resuming historical subagent threads spawns duplicate MCP and node_repl process stacks #37453

Open

Description

@MarkoAvreliy

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

Codex Desktop package: OpenAI.Codex_26.803.5235.0_x64

What subscription do you have?

This appears related to MCP lifecycle/refresh handling + sub agents

What platform is your computer?

No response

What issue are you seeing?

Summary

Codex Desktop on Windows creates duplicate local MCP and node_repl.exe process stacks when I open/resume historical chats that contain many completed subagents.

This occurs even when I do not send a new message or run a tool. Simply opening an old parent chat, and then opening a completed child thread in the Subagents panel, triggers thread/resume and starts another full set of local MCP processes.

The processes remain alive as children of the current codex.exe app-server instead of returning to a bounded baseline.

Environment

  • OS: Windows
  • Codex Desktop package: OpenAI.Codex_26.803.5235.0_x64
  • Main process:
    codex.exe -c features.code_mode_host=true app-server --analytics-default-enabled
  • Configured local stdio MCP servers:
    • MarkItDown
    • brain-memory
    • brain-knowledge
  • Also enabled: node_repl / Browser / Computer Use

Reproduction

  1. Fully quit Codex Desktop.
  2. Launch Codex Desktop and do not open historical chats initially.
  3. Baseline is one process stack:
    • 1 node_repl.exe
    • 1 MarkItDown MCP wrapper
    • 1 brain-memory MCP wrapper
    • 1 brain-knowledge MCP wrapper
  4. Open one historical parent chat with many old subagents. Do not send a message.
  5. The Subagents panel initially shows many old children as Working.
  6. Open one or more old child threads. Their displayed state immediately changes from Working to Done.
  7. Observe duplicate process stacks under the current codex.exe.

Observed result

After opening a historical parent chat and inspecting old child threads, the current codex.exe had:

  • 8 node_repl.exe direct children
  • 8 MarkItDown MCP wrapper processes
  • 8 brain-memory MCP wrapper processes
  • 8 brain-knowledge MCP wrapper processes
  • 32 direct helper processes total
  • 48 Python processes in the Codex process tree

No new user task, subagent launch, or MCP tool call was requested.

The local app-server log shows thread/resume at the same timestamps as MCP startup / tool catalog initialization, including:

app_server.request ... otel.name="thread/resume"
MCP server ... Processing request of type ListToolsRequest
codex_mcp::connection_manager::tool_catalog

### What steps can reproduce the bug?

## Summary

Codex Desktop on Windows creates duplicate local MCP and `node_repl.exe` process stacks when I open/resume historical chats that contain many completed subagents.

This occurs even when I do not send a new message or run a tool. Simply opening an old parent chat, and then opening a completed child thread in the Subagents panel, triggers `thread/resume` and starts another full set of local MCP processes.

The processes remain alive as children of the current `codex.exe app-server` instead of returning to a bounded baseline.

## Environment

- OS: Windows
- Codex Desktop package: `OpenAI.Codex_26.803.5235.0_x64`
- Main process:
  `codex.exe -c features.code_mode_host=true app-server --analytics-default-enabled`
- Configured local stdio MCP servers:
  - MarkItDown
  - brain-memory
  - brain-knowledge
- Also enabled: `node_repl` / Browser / Computer Use

## Reproduction

1. Fully quit Codex Desktop.
2. Launch Codex Desktop and do not open historical chats initially.
3. Baseline is one process stack:
   - 1 `node_repl.exe`
   - 1 MarkItDown MCP wrapper
   - 1 brain-memory MCP wrapper
   - 1 brain-knowledge MCP wrapper
4. Open one historical parent chat with many old subagents. Do not send a message.
5. The Subagents panel initially shows many old children as `Working`.
6. Open one or more old child threads. Their displayed state immediately changes from `Working` to `Done`.
7. Observe duplicate process stacks under the current `codex.exe`.

## Observed result

After opening a historical parent chat and inspecting old child threads, the current `codex.exe` had:

- 8 `node_repl.exe` direct children
- 8 MarkItDown MCP wrapper processes
- 8 brain-memory MCP wrapper processes
- 8 brain-knowledge MCP wrapper processes
- 32 direct helper processes total
- 48 Python processes in the Codex process tree

No new user task, subagent launch, or MCP tool call was requested.

The local app-server log shows `thread/resume` at the same timestamps as MCP startup / tool catalog initialization, including:

```text
app_server.request ... otel.name="thread/resume"
MCP server ... Processing request of type ListToolsRequest
codex_mcp::connection_manager::tool_catalog

### What is the expected behavior?

_No response_

### Additional information

Additional context
This appears related to MCP lifecycle/refresh handling. PR #19753 added explicit shutdown handling for MCP refresh and session shutdown:
https://github.com/openai/codex/pull/19753
Potentially related report:
https://github.com/openai/codex/issues/26869

Activity

  1. added
    appIssues related to the Codex desktop app
    on Aug 7, 2026
  2. added
    bugSomething isn't working
    windows-osIssues related to Codex on Windows systems
    mcpIssues related to the use of model context protocol (MCP) servers
    app-serverIssues involving app server protocol or interfaces
    subagentIssues involving subagents or multi-agent features
    on Aug 7, 2026
  3. MarkoAvreliy commented on Aug 7, 2026

    @MarkoAvreliy
    Author

    Additional confirmed reproduction:

    A clean Codex Desktop launch starts with one MCP/node_repl stack.

    Opening one historical parent chat with 51 stale subagents, without sending a message, causes thread/resume and creates additional full MCP stacks.

    Opening old child threads changes their UI state from Working to Done and triggers further thread/resume calls.

    Observed after one parent-chat load and inspection of a few children:

    • 8 node_repl.exe direct children
    • 8 MarkItDown MCP wrappers
    • 8 brain-memory MCP wrappers
    • 8 brain-knowledge MCP wrappers
    • 48 Python processes in the Codex process tree

    Codex logs correlate the launches with app_server thread/resume requests and MCP tool catalog initialization.

    This is reproducible without launching a new task, a new subagent, or an MCP tool call.

  4. leadingproblemsolver commented on Aug 7, 2026

    @leadingproblemsolver

    The strongest invariant here seems to be that opening historical state is not an execution transition.

    thread/resume currently appears to conflate two operations:

    hydrate/read historical thread for UI
    activate executable thread runtime + tools/MCP
    

    Those should be separate lifecycle intents. A historical child that is terminal should be loadable with something like:

    thread/open(view_mode='history')
      -> read rollout/state
      -> reconcile terminal status
      -> NO runtime lease
      -> NO node_repl
      -> NO MCP process/catalog startup
    

    Only an explicit continuation/new-turn operation should acquire an execution generation and materialize tool resources.

    That gives each thread instance a resource lease identity:

    thread_id
    execution_generation
    runtime_lease_id
    resource_set_hash
    lease_state = absent | active | released
    

    A terminal historical thread should have no active lease. Reopening it 20 times may create 20 view sessions, but must still create zero new MCP/runtime stacks.

    The reported Working->Done flip on opening a child is also a useful sign that read-time reconciliation is happening. That repair should stay read-only: discovering that historical state is terminal must not activate the runtime in order to prove it.

    Regression cases:

    open terminal child for inspection N times => 0 new MCP/node_repl stacks
    open parent containing 51 terminal children => 0 child runtime leases
    send new user turn to terminal/resumable thread => exactly 1 new execution generation/resources
    close/release generation                 => resources return to baseline
    restart app + inspect history            => still read-only
    

    The invariant is: history hydration may reconstruct evidence and UI state; only explicit execution intent may allocate live execution resources.

  5. MarkoAvreliy commented on Aug 8, 2026

    @MarkoAvreliy
    Author

    Follow-up: local mitigation, validation, and architectural request

    This is not a UI-only annoyance in my workflow.

    I reproduced the historical-hydration path where completed children can be presented as Working again and local tool/runtime activity can be re-materialized without any new user message. My earlier per-child stdio MCP topology amplified the process symptom, but it was not the root cause.

    Investigation cost

    Approximate workflow telemetry for diagnosis, reproduction, patching, and validation:

    • 10h 51m active investigation;
    • 7.36M internal agent-processing tokens.

    These are not exact billed API tokens or exact ChatGPT subscription-quota consumption; that cannot be reconstructed client-side. However, the incident materially affected productivity and forced a subscription-tier increase during the investigation period.

    Practical comparison

    I moved from Claude Code expecting comparable multi-agent capability with a more economical workflow. In my day-to-day Claude Code workflow, bounded subagent orchestration had been predictable and practical.

    In Codex Desktop, the equivalent workflow became operationally unsafe: reopening historical chats could restore stale child state and create uncertainty about whether completed work had truly stopped. This is my field experience, not a universal benchmark claim, but it is a major regression for a user relying on many small workers.

    Experimental local mitigation

    I published a sanitized, reproducible checkpoint:

    It does not disable MCP, browser control, computer use, Node tools, or subagents. Instead it keeps lifecycle boundaries intact:

    • historical display does not start MCP/tool runtime;
    • completed child status is restored without loading its runtime;
    • completed V2 child runtimes unload through the normal shutdown path;
    • stale legacy child edges are repaired without eagerly resuming the child;
    • tools still start normally for a real new foreground action.

    Validation: 4 lifecycle tests and 3 lazy-resume MCP integration tests passed. Opening four historical parent chats with completed children produced 0 new Python, 0 new Node, and 0 new node_repl processes, and no history-triggered MCP, child-spawn/resume, or model-transport references.

    This is an experimental local mitigation, not an official OpenAI fix or distributable Codex build.

    IMPORTANT FOR CODEX USERS AND MAINTAINERS

    HISTORICAL CHAT VIEWING MUST BE PASSIVE. A COMPLETED CHILD MUST NOT BE RESTORED AS A RUNNABLE RUNTIME JUST BECAUSE ITS PARENT CHAT WAS OPENED.

    DEFAULT CHILDREN SHOULD BE LIGHTWEIGHT AND DENY-BY-DEFAULT FOR MCP SERVERS, PLUGINS, SKILLS, AND OTHER HEAVY TOOLING.

    The orchestrator can retain its full toolset. Ordinary child workers should start with a minimal profile and bounded context; a specific MCP or plugin should be enabled only for the specialized child that actually needs it.

    Suggested model:

    • orchestrator -> required full toolset;
    • general research / coding / QA child -> no heavy MCP by default;
    • document child -> only document MCP;
    • memory child -> only memory MCP;
    • knowledge child -> only knowledge MCP;
    • browser/computer child -> only the relevant capability.

    If every child inherits the parent’s full tool surface and history by default, it creates an amplification risk: unnecessary process churn, configuration conflicts, avoidable context overhead, and potentially unnecessary agent/model usage.

    Codex needs first-class lifecycle isolation and a safe lightweight-child default, while preserving deliberate opt-in to specialist tools.

  6. dingaiminGIT commented on Aug 20, 2026

    @dingaiminGIT

    I reproduced the same restart/resume amplification on macOS. This appears to be the cross-platform counterpart of this issue, with a related live-session retention symptom tracked in #38925.

    I also reviewed the experimental mitigation described above. Its passive-history invariant matches my findings. The prototype below targets complementary paths that were still visible in my macOS restart captures: restored root sessions, internal summary sessions, concurrent cold tool-catalog discovery, and safe local-stdio retirement after a real turn.

    Environment

    • macOS arm64
    • ChatGPT Desktop 26.814.41407 (build 6720)
    • bundled codex-cli 0.148.0-alpha.15
    • 16 local stdio MCP servers, mostly uv + Python process pairs

    Observed behavior

    Before the mitigation, one capture contained 96 uv and 102 Python processes, using about 5.7 GB RSS in aggregate. During restarts the ChatGPT process reached 18–26 GB and macOS displayed the system out-of-memory dialog.

    The configuration contained one entry per MCP server. The duplicates were descendants of the same live app-server. In a bounded restart capture, five resumed threads produced eight complete MCP generations; each configured server appeared eight times as a launcher/runtime pair.

    A minimal observation sequence was:

    1. configure multiple local stdio MCP servers;
    2. fully quit and relaunch Codex Desktop with historical threads available for restoration;
    3. do not send a message;
    4. sample descendants of the new codex ... app-server by PPID and creation time;
    5. send one user message, then sample again after the turn completes.

    Before the prototype, multiple complete MCP groups appeared during step 3. After the prototype, the count remained zero until step 5.

    This is not only a final app-server shutdown problem. The amplification has multiple sources:

    1. restored threads construct an MCP runtime and eagerly discover optional local stdio tools before a user turn needs them;
    2. a cold tool-catalog cache permits concurrent sessions to probe the same local stdio configuration;
    3. the local session Environment object identity prevents otherwise identical host-local catalogs from sharing across sessions;
    4. internal ephemeral system sessions used for thread summaries can receive the normal local stdio MCP configuration;
    5. superseded/idle local stdio connection sets can remain alive after a turn unless retirement is tied to the actual turn owner and in-flight bindings.

    Prototype and validation

    I built a version-matched prototype with the following behavior:

    • restored threads publish local stdio MCP connections as dormant until a real turn;
    • optional host-local stdio discovery is process-scoped singleflight using a complete identity (server, transport, args/env, cwd, environment/capability fingerprint); different cwd values and remote environments remain isolated;
    • the known Desktop thread-summary contract skips only host-local stdio MCPs for ephemeral=true, threadSource=Feature("system"), and plugins disabled; explicit subagents, HTTP MCPs, Codex Apps, and remote stdio are preserved;
    • turn completion performs a cold swap: it publishes a dormant set first, lets existing bindings retain the old set, and retires only host-local stdio after the old references drain;
    • normal and abort paths use a turn generation/owner, so an old cancellation cannot finish a newer turn;
    • probe failure/cancellation releases only its own generation lease, avoiding both permanent suppression and duplicate slow starts.

    The prototype maps these behaviors to existing ownership points rather than to Code Mode cell teardown:

    • core::session::mcp_runtime selects the startup policy from thread restore/ephemeral context and owns turn begin/finish transitions;
    • codex_mcp::runtime::McpRuntime publishes connection generations and performs the dormant cold swap;
    • McpToolCatalogCache coordinates an optional-startup owner per complete local identity;
    • McpConnectionManager::shutdown_local_stdio retires only host-local stdio transports after old bindings drain.

    I initially considered shutting MCP down after each Code Mode execution cell, but source and PID tests showed that the MCP runtime is session/thread-owned. Cell-scoped shutdown would be too early and could break later MCP calls in the same turn or session.

    Focused validation included:

    • 184/184 codex-mcp unit tests;
    • real PID tests for resume-without-start, first-turn activation, superseded-generation cleanup, and hidden summary sessions;
    • negative coverage for explicit subagents/root sessions, different cwd values, HTTP MCP, and remote stdio;
    • startup failure, cancellation, and unrelated-connection failure races;
    • Clippy with -D warnings for the affected crates and an arm64 release build.

    In the post-change restart capture, the new app-server had zero local MCP/uv processes until the first user turn. That turn created one MCP group for its cwd. Three internal summary sessions created no additional groups. A concurrent task in a different cwd created a second group as expected, and that group disappeared after its turn. A normal quit followed by an immediate reopen did not require waiting for Python/uv processes manually; the old group reached zero before the new app-server activated a group.

    Proposed upstream shape

    I have a working prototype, but it is cumulative, version-bound, and based on an older bundled alpha. I am intentionally not posting that diff as an unsolicited PR. If this direction matches the intended ownership model, I can rebase/reimplement it on current main and split it into focused changes:

    1. explicit shutdown for temporary resource/status managers with stdio PID tests;
    2. a dormant startup policy for restored threads;
    3. process-scoped optional-startup singleflight with complete identity and race tests;
    4. safe turn-end retirement of host-local stdio connections;
    5. an explicit generic MCP policy for Desktop internal generation, rather than upstreaming a hard-coded Feature("system") check.

    Would maintainers be interested in this direction, and if so, which slice would you prefer as the first invited PR?

    No internal server names, credentials, command arguments, or private paths need to be included in an upstream change.

  7. PracticalSwan commented on Sep 6, 2026

    @PracticalSwan

    Fresh reproduction on the current Windows Desktop package confirms this is still present in OpenAI.Codex 26.901.5280.0 / bundled codex-cli 0.153.4.

    From one long-lived Desktop codex.exe app-server process, a read-only process-tree snapshot currently shows multiple helper generations still resident simultaneously:

    • 4 direct node_repl.exe children created at different thread/runtime generations;
    • 5 direct unified-computer-use node.exe children;
    • 5 app-tools MCP cmd.exe launcher children;
    • 5 codex-security MCP launcher children;
    • multiple repeated configured MCP stacks (including Serena, browser/notebook/codegraph tooling, etc.).

    The generations have distinct creation times under the same app-server process (09:00, 16:48, 18:32, 19:05 local time on 2026-09-06), rather than replacing/releasing the prior generation.

    A particularly useful lifecycle data point comes from a normal V2 review child in a completed multi-agent session:

    • its helper generation was created when the child became active at ~18:32 local;
    • the child completed both of its recorded turns successfully;
    • after completion, the generation's node_repl, CUA, app-tools/security MCP and configured MCP processes were still resident under the parent app-server.

    Separately, the app-server log at another generation boundary shows thread/resume followed immediately by session initialization/tool-runtime activity, consistent with the original report.

    So on the current build this is not only a stale UI symptom: completed/resumed thread runtimes can leave process resources resident instead of returning toward the app-server baseline. This supports the invariant already proposed in this issue: historical hydration should be passive, and an execution generation should release its owned tool/runtime resources when the thread/child becomes terminal.

    No processes were terminated for this check; this is observation-only.

  8. PracticalSwan commented on Sep 6, 2026

    @PracticalSwan

    Reproduces on a newer Codex Desktop update as well.

    Environment:

    • Windows Codex Desktop package: OpenAI.Codex 26.901.6511.0
    • bundled/app-server CLI: codex-cli 0.153.4
    • app-server RPC client version: 26.901.51231

    Fresh app-server start was at 23:49:59 local. At 23:57:31 the Desktop issued one thread/resume for a historical thread. In the following 25-second window there were 0 turn/start requests for that resumed thread, but the trace immediately entered:

    resume_thread_with_history
      -> thread_spawn
      -> session_init
      -> mcp_manager_init / mcp.runtime.refresh
      -> startup_prewarm / MCP tool catalog
    

    The OS process tree shows a corresponding runtime/MCP process wave beginning at 23:57:32. At 23:57:41 Codex logged the resumed session's MCP server tasks being cancelled, then created a separate thread/start runtime wave. A later process-tree snapshot still contained descendants from both creation waves, including multiple node_repl and stdio MCP processes.

    So the updated build still violates the lifecycle invariant described here: history resume/hydration can materialize executable tool runtime before any model turn is started, and cancelling the logical MCP tasks did not return the OS process tree to a single bounded runtime baseline in this observation.

    No prompts, credentials, repository contents, or private MCP configuration are included in this report.

  9. PracticalSwan commented on Sep 9, 2026

    @PracticalSwan

    Current-version Windows reproduction with a completed subagent, using process creation times plus durable thread state:

    • Codex Desktop AppX 26.903.8094.0
    • bundled/global runtime 0.153.4
    • one current Desktop app-server process

    The parent Desktop session created its initial local-helper cohort at about 01:21:24 local time. Direct children of the app-server in that cohort currently include:

    • 1 blender-mcp.exe
    • 5 cmd.exe
    • 5 node.exe
    • 1 node_repl.exe
    • 1 uvx.exe

    At 19:07:24Z (02:07:24 local), a subagent started. At that exact second, a second direct-child cohort with the same counts and executable classes was created under the same app-server.

    The child rollout itself is durably terminal and healthy:

    • 194/194 records parse
    • ordinals 0..193, no gaps/duplicates/backwards transitions
    • final task_complete at 19:15:02.070Z
    • history projection caught up exactly to EOF
    • no later child records

    At 04:28 local, more than two hours after that child completed, the entire 02:07 helper cohort was still alive alongside the original 01:21 cohort. The parent turn had also become terminal earlier; no restart or cleanup was performed as part of this observation.

    This is stronger than a process-name-only correlation because the duplicate cohort creation timestamp matches the child-thread start timestamp to the second, while the child has a verified completed durable state. It appears consistent with the MCP/helper lifetime leak described here on a much newer Desktop/runtime build.

    I did not terminate these processes because they share the current Desktop app-server and there is unrelated concurrent work on the machine. No private thread IDs, prompts, project names, local data paths, credentials, or process command arguments are included here.

  10. PracticalSwan commented on Sep 10, 2026

    @PracticalSwan

    Still reproducible on Windows Desktop 26.903.9818.0

    Current Store build evidence shows the historical-thread hydration behavior remains present after updating from 26.903.8094.0 to 26.903.9818.0 (Desktop-owned app-server still reports 0.153.4).

    On a clean launch, the Desktop app-server started without duplicate thread-specific MCP cohorts. I then opened an existing historical thread only to recover/read its already-durable history; no model prompt was sent. The log sequence was:

    maybe_resume_started conversationId=<historical-thread>
    ...
    mcp_server_startup_status_updated ... status=starting threadId=<same-thread>
    ...
    method=thread/resume ... errorCode=null
    

    That hydration started a full configured MCP/tool cohort (including node_repl, Browser/Computer Use runtime, CodeGraph, Blender, Playwright, Serena and other configured servers). The thread's durable latest turn is completed and its history projection is at EOF, but the corresponding helper processes remained direct/indirect children of the same Desktop app-server more than 20 minutes later.

    Two additional historical threads opened read-only during the same recovery pass created additional cohorts at their respective thread/resume timestamps; those processes likewise remained attached to the app-server after the histories were fully materialized and terminal/latest state was known.

    I did not terminate any helper process because other Desktop work may be active and process age alone is not a safe ownership boundary. This is therefore observation-only current-build evidence: thread/resume used for historical hydration still allocates executable MCP/tool resources and those resources are not promptly released when the hydrated thread is already terminal.

    No account identifiers, transcript content, credentials, or absolute private paths are included.

  11. terragridrtk commented on Sep 10, 2026

    @terragridrtk

    Additional macOS evidence for this issue, with a possible bulk-rename trigger. Related: #40579.

    Environment

    • ChatGPT/Codex desktop 26.903.71938, build 8576
    • Bundled codex-cli 0.153.4
    • macOS 26.6.2, arm64, 16 GiB physical RAM

    Observed incident

    During a user-authorized workflow renaming existing local threads through the native set_thread_title tool, macOS displayed “Your system has run out of application memory” and attributed 17.43 GB to ChatGPT. The user reports the same memory-exhaustion behavior during an earlier bulk-thread-management run. The rename tools stopped returning partway through the run; 58 of 83 selected threads had the new category prefix when checked read-only. Calls were awaited sequentially, not dispatched in parallel. This was an operational incident, not a controlled from-clean-start reproduction; earlier thread operations had also occurred in the app session.

    After the user interrupted/recovered the work, a process-tree snapshot still showed approximately 428 app-descendant processes. Repeated sets included approximately:

    • 65 instances of a configured local stdio MCP executable
    • 65 uv wrappers with their Python MCP children
    • 65 npm exec @playwright/mcp wrappers with Node children
    • 65 direct node_repl children of the same app-server

    Summing proc_pid_rusage(pid, RUSAGE_INFO_V0).ri_phys_footprint over the app processes and all recursive descendants, including externally installed Node/Python binaries, yielded 18,848,910,240 bytes (~17.55 GiB). kern.memorystatus_vm_pressure_level was 2. This sum is a separate point-in-time metric, not an assertion of exact equivalence to the earlier macOS dialog value. Summing RSS only within the application bundle had substantially undercounted the problem.

    The local preparation scripts streamed transcript files line by line. No actual repository/database migration or restore-test database was executed in this round. We stopped further runtime-loading/native bulk actions rather than deliberately reproducing another OOM.

    Bounded static observations from the installed desktop JavaScript

    • The visible native set_thread_title handler awaits the title persistence path (thread/name/set), then invokes a description-refresh path without awaiting it. That path reaches the optional native bridge threadMetadataGeneration.generateDescription. Consequently, sequential title-tool acknowledgements do not establish completion of the resulting description-generation work.
    • The visible inactive-thread unsubscriber uses a one-hour TTL. Its candidate selector computes overage above a nominal limit of four inactive owner threads, but the examined return path selects TTL-expired candidates only; overage is logged rather than used to select additional candidates for cleanup.
    • An internal thread/unsubscribe path exists. We did not invoke private UI methods, patch the bundle, archive threads, or kill unidentified MCP processes as a workaround.

    Uncertainty / requested investigation

    We have not established that the native description-generation bridge itself starts every observed MCP stack, nor that all these processes are permanently leaked. Its implementation was not available in the inspected JavaScript. The repeated process stacks and high memory usage are measured; the causal link from bulk rename through metadata generation to runtime retention remains a hypothesis.

    Could the team check whether metadata-only title/description operations unnecessarily initialize tool-enabled sessions, whether inactive-runtime limits are enforced before the one-hour TTL, and whether all MCP descendants are reclaimed when temporary metadata work finishes? A bounded metadata-only rename path would be valuable for thread organization without loading each thread runtime.

    This report intentionally omits conversation content/IDs, customer data, local project paths, credentials, full configuration, and raw logs.

    Additional question about model usage: The user also reports unexpectedly rapid account-quota consumption during these workflows. We have not attributed quota usage to metadata generation or measured its billing. Please check whether title-triggered description generation performs additional inference and how that work is accounted for. MCP process count or RAM usage alone does not demonstrate token consumption.

  12. PracticalSwan commented on Sep 11, 2026

    @PracticalSwan

    Latest-session lifecycle evidence on Windows Desktop 26.903.9818.0

    A newer real multi-agent session reproduces the resource-retention side of this issue on the same Store build (Desktop-owned app-server still reports 0.153.4).

    Before cleanup, durable state showed the parent’s latest turn completed, all ten child rollouts were structurally valid and projected to EOF, there were 0 recently updated in-progress turns, and Codex’s saved process-manager records mapped to 0 still-live tracked commands. Router activity was also idle.

    Despite that terminal/quiescent state, the Desktop root still owned a 225-process subtree. Representative counts were:

    • 90 node.exe
    • 49 cmd.exe
    • 28 python.exe
    • 14 node_repl.exe
    • 7 uvx.exe
    • 7 uv.exe
    • 7 Serena processes
    • 7 blender-mcp.exe
    • 2 nested codex.exe app-server processes

    One nested app-server could be traced through node_repl -> unified-computer-use node -> Desktop app-server; its creation time matched the parent session start exactly, so it was not an unrelated global CLI process.

    After proving quiescence, I restarted only the exact Store-app root process. All old parent/child helper PIDs disappeared. The freshly relaunched Desktop settled to a 41-process subtree with one app-server and one normal startup tool cohort (13 node, 7 cmd, 4 python, 2 node_repl, one each of uv/uvx/Serena/Blender).

    So this current build still retains completed execution/tool cohorts long after durable work is terminal, and those resources are reclaimed by an app restart rather than normal thread completion. This complements the earlier historical-hydration reproduction: the leak is visible after a real completed multi-agent execution as well, not only after read-only thread/resume hydration.

    No private thread IDs, prompts, project names, credentials, absolute user-data paths, or command payloads are included.

  13. PracticalSwan commented on Sep 13, 2026

    @PracticalSwan

    Fresh current-build evidence from Windows, observed 2026-09-14 local time:

    • Codex Desktop AppX: 26.908.4834.0
    • Desktop-owned copied app-server: 0.154.0-alpha.6.2 (coreRuntimeInUse=false)
    • One Desktop root currently retains a 158-process subtree.
    • Under its main codex.exe app-server, the retained tool/runtime population includes 8 node_repl.exe, 5 Blender MCP wrappers, 5 Serena wrappers, and a second Desktop-binary codex.exe app-server --listen stdio:// nested under a node_repl branch.
    • Tool/runtime cohorts were created at multiple distinct times during the same Desktop lifetime rather than returning to the earlier bounded baseline.
    • Read-only structured app-server logs since this launch contain 572 MCP server log events, including dense later bursts. The current Desktop UI log still has 0 panic/fatal/crash records.

    This shows that the duplicate/retained local runtime symptom still exists on 26.908.4834 / 0.154.0-alpha.6.2. I am not claiming this pass proved the exact passive-history-only trigger again: the current structured log does not contain a thread/resume string, and I did not deliberately reopen historical children solely to reproduce it because active work is using the process tree. No process was killed or restarted.

    This update contains only aggregate process/log metadata; no thread IDs, prompts, paths from task payloads, credentials, or conversation content.

  14. PracticalSwan commented on Sep 15, 2026

    @PracticalSwan

    Fresh persistence evidence on Microsoft Store build 26.908.9136.0 (Windows x64), after many local sessions had completed.

    I first verified the local thread store read-only before touching Desktop:

    • 462 unarchived thread rows;
    • every thread updated in the preceding 8 hours was terminal;
    • no thread whose rollout tail still looked running had been updated since September 14 06:00 local time;
    • global projection audit had 0 critical faults.

    Despite that quiescent state, the running Desktop app-server owned 33 direct child processes / 86 descendants, including 3 node_repl.exe, 2 Blender MCP processes, 3 Serena launchers, and repeated stdio MCP wrappers.

    I then performed an exact Desktop-only quit/relaunch, preserving four unrelated Codex app-servers (three global/tunnel-owned plus one VS Code-owned). The quit cleared all Store-app descendants.

    On the fresh 26.908.9136.0 relaunch, before sending any user turn, the single Desktop app-server immediately created 25 direct children including two complete tool-runtime cohorts:

    • 2 node_repl.exe;
    • 2 Blender MCP processes;
    • 2 Serena launchers;
    • duplicated Codex app tools/security/LSP/Playwright/NotebookLM/CodeGraph stacks.

    Read-only structured app-server logs from this fresh generation contain 0 thread/start and 0 thread/resume requests. Desktop logs show both primary and avatarOverlay renderer surfaces connecting during startup; the duplicated cohorts appear at the same startup interval. I am not claiming those renderer surfaces are proven causal, only that the duplication now reproduces without a task start/resume and survives in the current Store build.

    This makes the current symptom broader than leftover processes from long-running historical sessions: a clean Desktop relaunch can allocate duplicate MCP/tool stacks before execution intent. I did not modify package files, thread state, MCP configuration, or WindowsApps contents.

  15. PracticalSwan commented on Sep 19, 2026

    @PracticalSwan

    Current-build lifecycle follow-up on Windows Desktop 26.915.4065.0 / Desktop-owned app-server 0.155.0-alpha.9.2:

    • I fully quit the exact Store Desktop tree after proving the active router was idle and the affected parent/children were terminal/projected to EOF, then relaunched Desktop.
    • On relaunch, Desktop issued one thread/resume for the previously failed parent. There were 0 thread/start records in the fresh Desktop log.
    • During that resume window, the single Desktop app-server created several near-simultaneous tool/runtime cohorts: multiple node_repl processes, repeated Blender MCP trees, repeated CUA/app-tools/security/configured MCP launch stacks, and repeated configured-tool processes under the same app-server.
    • This is not a pure idle-start reproduction: the fresh log does contain the one thread/resume, so I am explicitly narrowing the trigger to historical/failed-thread hydration here.
    • The duplicate population was transient in this run. A later read-only process-tree check showed the extra cohorts had mostly drained back toward one normal tool cohort rather than remaining indefinitely.
    • No model prompt was sent to reproduce this lifecycle behavior, and no helper process was individually terminated.

    So the newest Store build still shows the same resume/hydration -> executable helper materialization path. In this observation cleanup eventually occurred, which is an improvement over the long-lived retained-cohort cases reported above, but one resumed failed thread still temporarily spawned multiple full runtime/MCP cohorts rather than one bounded generation.

    No private thread IDs, prompts, project names, credentials, or task payloads are included.

  16. PracticalSwan commented on Sep 21, 2026

    @PracticalSwan

    Current-version recurrence on Windows Codex Desktop 26.915.4065.0 / bundled app-server 0.155.0-alpha.9.2 (2026-09-21): after an exact Desktop-only restart, the bundled app-server initially settled to only its conhost child. Reopening a historical thread then appended a resume record without starting a new turn, and a new MCP/node_repl process cohort appeared under that same app-server. The rollout remained terminal and projected exactly to EOF, so this was not active work or transcript corruption. External Codex/app-server trees were separate and untouched. This confirms the resume-triggered process-retention pattern still reproduces on 26.915.4065.0.

  17. DiscoverAlbania commented on Sep 26, 2026

    @DiscoverAlbania

    Fresh reproduction on Windows Codex Desktop 26.924.2738.0 (2026-09-26, Europe/Tirane). This is the same resume-time helper duplication pattern on a newer Store build.

    After a full Desktop restart, the codex.exe app-server launched at 12:17:51. In its first 60 seconds, read-only logs_2.sqlite inspection found thread/resume records for 10 distinct thread IDs and thread/start for one. The helper suites appeared while Desktop restored those chats, before a user tool request. This timing is strong correlation, though it does not alone prove which Desktop call triggers each helper.

    At 12:33:54, the same app-server tree had 170 processes and 4,345 MiB summed private bytes. Sanitized PID/parent/start-time inventory (no command lines or credentials) showed:

    Group Processes Private MiB
    node_repl.exe 22 55
    computer-use Node runtime 22 539
    creative-production MCP Node runtime 11 431
    Make mcp-remote Node runtime 22 1,689
    Make cmd.exe wrappers 11 31
    Make powershell.exe launchers 11 671
    conhost.exe 57 268

    Nine of the retained Make PowerShell launchers started at 12:18:23–12:18:35; two more started at 12:29:11 and 12:29:46 as chats were used later. So there is both an immediate startup burst and subsequent cohort churn. Asana and Chrome DevTools MCPs had been disabled and were absent after restart, which suggests the issue is repeated initialization of enabled runtimes rather than a failure to apply those toggles.

    I have locally replaced Make's stdio powershell -> npx mcp-remote setup with Codex's native streamable HTTP MCP configuration. A fresh ephemeral Codex CLI session successfully invoked Make organizations_list via native HTTP. That removes one costly local bridge after the next Desktop restart, but should not affect the repeated computer-use, creative-production, and node_repl cohorts.

    Suggested regression cases: restore 10 terminal/dormant chats and confirm no full MCP/REPL suite starts until a turn needs it; exercise one active tool turn and confirm one bounded suite; switch among saved chats and complete turns without monotonic process growth; unload/replace a runtime and confirm its Windows child process tree exits. Preserve runtimes for genuinely running background work.

  18. DiscoverAlbania commented on Sep 26, 2026

    @DiscoverAlbania

    Post-restart verification on the same Windows Desktop build (26.924.2738.0):

    • Changed only the Make MCP transport to native streamable HTTP (https://eu1.make.com/mcp, bearer_token_env_var=MAKE_MCP_AUTH_HEADER). The Make plugin and REST access remain enabled.
    • Fully quit and relaunched Desktop. The old app-server PID exited. No process command line matching the former Make mcp-remote bridge remained anywhere in the process list.
    • At 3m36s after relaunch, the new main app-server tree had 125 processes / 1,966 MiB private bytes, versus 170 / 4,345 MiB before restart. A live Make organizations_list MCP call succeeded through native HTTP, as did a separate Make REST read and Airtable app ping.
    • The product-level duplication remains: the new tree already had 12 creative-production MCP Node processes, 24 computer-use Node processes, and 24 node_repl.exe. After brief read-only tool checks, it reached 129 processes / 2,247 MiB, including a nested codex.exe app-server under a tool runtime.

    The local transport change removed the repeated Make wrapper cost (previously 22 Make Node processes at ~1,689 MiB private plus 11 PowerShell launchers at ~671 MiB). It does not address the per-chat runtime initialization/retention tracked in this issue. The process and memory figures are summed private bytes for the main app-server descendant tree, not Task Manager's whole-system RAM percentage.

  19. saiafgamal commented on Oct 7, 2026

    @saiafgamal

    I am also observing duplicate MCP process stacks on macOS with completed chats resuming during desktop startup. I have not isolated whether the restoration path is identical to the historical-subagent path in this report, but the process-level result appears related.

    Environment

    • Desktop app: 26.930.61225, build 13232.
    • Bundled Codex CLI: 0.160.1.
    • macOS: 27.0.1 (26A434), Apple silicon.
    • RAM: 16 GB.
    • Measurements collected on October 7, 2026.

    Observed behavior

    1. Fully quit and reopen the desktop app in an existing installation with local STDIO MCP servers enabled.
    2. Return to the chat used for diagnosis.
    3. The launch log shows 4 other completed chats resuming before the selected chat. Their maybe_resume_success entries report latestTurnStatus=completed and markedStreaming=true.
    4. MCP startup notifications occur across 6 distinct chat IDs.
    5. Process-tree inspection confirms 6 running copies of each of 6 custom connector configurations, plus 6 Playwright MCP npm launchers.

    This is an observed reproduction in an existing installation, not a minimal clean-install reproduction. The exact reason the other completed chats become loaded has not been established.

    Process and resource measurements

    • 4 separately configured WordPress connectors: 24 Node processes total (6 per configuration).
    • Mail MCP bridge: 6 Node processes.
    • Chrome DevTools MCP: 6 Node processes.
    • Playwright MCP: 6 npm exec @playwright/mcp@latest launchers.
    • Total for the 6 custom configurations: 36 connector processes.
    • Earlier renderer samples reached 92% CPU and approximately 2.6 GB memory footprint.
    • The bundled backend's process sample reported a 4.1 GB peak memory footprint.
    • Later samples settled to lower CPU usage. These snapshots do not establish a sustained 92% CPU load or capture the full startup peak on every launch.

    Mitigations tested

    • Disabled automatic connection to an offline remote host. Its retry errors stopped, but completed-chat restoration and duplicate connector stacks persisted on the next launch.
    • Replaced npx ...@latest launchers for the 6 custom configurations with direct Node launchers using fixed copies of the same installed package versions. All 6 connectors still initialized and returned their tool catalogs.
    • A representative WordPress comparison returned identical tool catalogs and measured 0.641 CPU-seconds through npm versus 0.277 CPU-seconds through direct Node. Elapsed connection time was similar. This is a single measured comparison, not a statistically robust benchmark.
    • After restarting with those launchers, process inspection still showed the same 6 copies per custom configuration. Removing npm overhead did not resolve the lifecycle duplication.

    Expected behavior

    Completed/inactive chats should not retain unbounded or unnecessary connector stacks. Please investigate whether these startups and process lifetimes are intended. On-demand startup or explicit MCP start/stop controls would help keep useful connectors configured without incurring their cost for every loaded chat.

    Related reports: #50627 (repeated readiness/inventory refresh traffic on macOS) and #42242 (lazy/on-demand MCP startup).

    In-app feedback submission also failed

    Two attempts at 08:35:49 and 08:36:44 Europe/Istanbul returned error code -32603:

    failed to upload feedback to Sentry
    client error (Connect): connection closed via error
    

    The UI displayed “Feedback upload not confirmed.” A basic HTTPS connection test from this Mac reached the service, but that does not explain or reproduce the app's failed upload requests.

    Only sanitized measurements are included here. No raw logs, chat IDs/content, connector endpoints, credentials, or configuration files are attached.

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 appapp-serverIssues involving app server protocol or interfacesbugSomething isn't workingmcpIssues related to the use of model context protocol (MCP) serversperformancesubagentIssues involving subagents or multi-agent featureswindows-osIssues related to Codex on Windows systems

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions