Skip to content

[Windows 10] DWM Composition handles accumulate after Codex tasks with tool calls #33192

Closed

Description

@J-ShuJie

Summary

On Windows 10 22H2, I can reproduce persistent growth in DWM's Composition handle count after Codex tasks that make terminal tool calls. A no-tool task that replied only OK did not show the same growth. In one controlled five-call run, the Composition count increased by 22 and remained elevated throughout the 30-second period after the task finished.

After about four days of normal Codex use, DWM had 22,770 Composition handles, compared with 817 after a DWM restart. At that point, dragging an ordinary window could lag by 10 to 20 seconds. Restarting only DWM cleared the accumulated state and immediately restored smooth window movement.

In a separate control, every ChatGPT.exe and codex.exe process was absent for about 5 minutes 35 seconds. No comparable new growth was observed while Codex was closed, but the handles already held by DWM did not fall. So far, they have only been cleared by restarting DWM or Windows.

I cannot yet tell whether the lifetime problem begins in the Codex UI, Electron, Chromium, DirectComposition, DWM, or the graphics driver. This report documents the repeatable trigger and the resource signature.

Environment

  • Windows 10 22H2, build 19045.7417, x64
  • Codex MSIX OpenAI.Codex_26.707.9981.0_x64
  • Internal app version 26.707.72221, build 5307
  • Electron 42.1.0
  • Chromium/ProductVersion 150.0.7871.115
  • One internal display at 2400 x 1600, 60 Hz; no external display
  • Intel Iris Xe Graphics and NVIDIA GeForce RTX 3050 Ti Laptop GPU
  • HAGS enabled (HwSchMode=2)
  • Variable refresh optimization enabled (VRROptimizeEnable=1)
  • MPO disabled (OverlayTestMode=5)

I have seen the same general problem across several Codex desktop updates. The version above is the one used for the controlled captures below.

Reproduction

  1. Start from a low DWM handle count after signing in to Windows or restarting DWM.

  2. Open Codex and create a new task.

  3. Ask it to make several separate, read-only terminal calls, for example:

    Get-Date
    Get-Location
    Get-Process -Id $PID | Select-Object Id, ProcessName, HandleCount
    git status --short
    Get-ChildItem -Name | Select-Object -First 3
  4. Record the count and type of DWM handles before the task, while it runs, and for at least 30 seconds after it finishes.

  5. Repeat the task a few times. It is not necessary to keep Codex in the foreground.

  6. For comparison, create a new task that only replies OK and does not call any tools.

Expected: After a task finishes and its status UI settles, DWM Composition handles should return close to their previous baseline.

Actual: Tasks with several tool calls leave a small persistent net increase. Repeating these task cycles gradually builds a much larger DWM-side handle stock.

Controlled results

Test Workload Persistent DWM handle change Composition change
Plain text control New task replied only OK; no tools +5 -1
Five terminal calls Five separate read-only calls +10 +22
Idle long task view Visible and idle for about 10 minutes +2 Not sampled by type
Idle short task view Visible and idle for about 10 minutes 0 Not sampled by type
Active task Running for about 6 minutes +66 Not sampled by type
Active task, Codex minimized Running in the background +55 Not sampled by type
Scheduled task Natural unattended run +88 Not sampled by type

The two controlled runs used 30-second medians before and after the task. The DWM PID remained unchanged.

DirectComposition trace

I captured the five-call test with the Microsoft-Windows-DirectComposition and Dwm-Core ETW providers.

During the server-reported task interval, DirectComposition recorded four identical 12-resource creation batches for DWM channel 61. None of those 48 resources had a matching release before the trace ended. An identical fifth batch appeared about 2.1 seconds before the server startedAt timestamp, which may reflect local task initialization, but I cannot prove that from this trace.

Every batch had this resource type sequence:

201, 195, 195, 32, 32, 79, 87, 39, 58, 39, 58, 30

The resource handles were sequential and had openShared=false.

This capture did not include the Dwm-Core channel-attribution keywords, so I cannot map channel 61 to a specific client PID. The same batch signature also appeared occasionally outside the task interval. I therefore treat this as a correlation clue rather than proof that channel 61 belongs to Codex.

The capture used Dwm-Core keyword mask 0x21. A follow-up trace should use 0x900021 so that it also records PROCESS_ATTRIBUTION, CHANNEL_OPENED_FOR_PROCESS, CHANNEL_CLOSED_FOR_PROCESS, and COMMAND_PROCESSED_ONBEHALF. The DirectComposition mask can remain 0x5F.

DWM kept the same PID throughout the run. There was no lock-screen transition. Video.UI emitted no DirectComposition create or release events during the task interval, and the ShellExperienceHost burst began only after completion, consistent with the completion notification.

Related report

The closest existing report I found is #16374, which describes progressive Windows shell freezes associated with Codex. This may be part of the same broader problem. The additional evidence here is the DWM handle type, the no-tool control, and the DirectComposition capture.

Package note

The affected package is an Electron 42 and Chromium 150 application, not WebView2. I found task-status and sidebar lifecycle code in the packaged ASAR, but the production package has no source maps, so I could not map the observed resource lifetime to a specific source module.

Evidence retained locally

The most relevant sanitized results are included above. I have also kept the complete ETL, the two run summaries, before-and-after handle type diffs, and the extracted DirectComposition batches. I have not attached the raw ETL because it contains a wider process timeline.

Activity

  1. added
    bugSomething isn't working
    appIssues related to the Codex desktop app
    windows-osIssues related to Codex on Windows systems
    on Jul 14, 2026
  2. codengine commented on Jul 19, 2026

    @codengine

    This issue also affects the CLI version of Codex, and it has been like this at least since 0.13*

  3. codengine commented on Jul 19, 2026

    @codengine

    I let Codex analyze the issue and it figured out that it may relate to this commit:
    6b3d82d

    ... which creates a private desktop per tool call and ignores the return value when it is closed. I am testing a custom build where a shared/reused private desktop is used and will report back

  4. codengine commented on Jul 19, 2026

    @codengine

    Codex created this patch. I deployed the executables and ran thousands of tool calls. The memory and CPU consumption of dwm.exe did not move.

    The main cause seems to be that every tool call creates a dedicated private desktop and it's closure is neither checked nor retried.
    The attached patch, based on 0.144.5, creates one private desktop per Codex process.

    codex-0.144.5-shared-private-desktop.patch

  5. codengine commented on Jul 20, 2026

    @codengine

    10 hours later with 4 agents running in parallel - dwm still only reports 46 MB, load sub 0.1%, all windows are snappy... the main issue is really the desktop creation per tool call and the missing cleanup, or there is a leak in dwm.exe itself.

  6. ManorianOTP commented on Jul 30, 2026

    @ManorianOTP

    Adding another Windows reproduction on a current desktop build.

    Environment

    • Codex/ChatGPT Desktop package: 26.721.4979.0
    • Windows x64 build: 26200.8875 (25H2)
    • Installed RAM: 32 GiB

    Symptom

    This is system-wide compositor/input lag, not only a slow Codex window:

    • Composer typing has some latency from launch.
    • After prolonged long-running Codex work, unrelated Windows applications and desktop window movement become severely delayed.
    • After releasing a dragged ordinary window, it can continue moving visually for approximately 10 seconds or more.
    • The slowdown worsens over hours while Codex remains open.

    Resource headroom

    A live read-only snapshot during the affected session showed approximately:

    • 13.4 GiB physical RAM still available
    • 23% overall CPU load
    • no large population of runaway taskkill.exe or conhost.exe processes

    This therefore did not coincide with conventional CPU or physical-memory exhaustion.

    The symptom is materially similar to the delayed ordinary-window dragging reported in this issue. It may also overlap with long-thread renderer/event-state growth discussed in #21134, but the important distinction here is that the rest of Windows becomes nearly unusable too.

    Expected behavior

    Long-running local Codex workflows should not degrade Windows DWM/compositor or system-wide input responsiveness when the machine retains substantial CPU and RAM headroom. DirectComposition/DWM and renderer resources should remain bounded and be released during prolonged background work.

    Private prompts, project names, session identifiers, filesystem paths, and raw logs are intentionally omitted. Privacy-safe diagnostics can be collected through the official feedback channel if maintainers specify what additional counters or trace profile would be useful.

  7. josh-robey commented on Aug 8, 2026

    @josh-robey

    Confirming this is still unfixed in the current Store build, with a concrete Windows 10 reproduction.

    Environment

    • OS: Windows 10, x64 (22H2 family)
    • Codex/ChatGPT Store package: OpenAI.Codex_26.803.5235.0_x64__2p2nqsd0c76g0 (current as of Aug 6)
    • The bundled app\resources\codex.exe still imports CreateDesktopW / CloseDesktop, and the sandbox helper (codex-command-runner.exe) still accepts --windows-sandbox-private-desktop and is re-copied into %USERPROFILE%\.codex\.sandbox-bin\ on every launch.

    Observed behavior

    • After a few days of normal Codex use (tool-call-heavy sessions), DWM's Composition handle count climbs into the tens of thousands.
    • System-wide input lag follows: window dragging takes 10-20 seconds to respond, unrelated apps stall, and mouse movement degrades.
    • All other metrics are clean while this happens: CPU ~30%, disk queue 0, available RAM in the multi-GB range, GPU well under 20%.
    • Restarting dwm.exe clears the handles and immediately restores responsiveness. Nothing else does - not a GPU driver reset, not killing the renderer tree.

    Timeline impact
    This has been slowing my PC down for literally 6-7 months. I have been trying to figure out what has been happening the whole time, and it has been progressively getting worse. This needs to be fixed ASAP please - it is killing my PC.

    What would help

    • The per-tool-call private desktop creation path (introduced in commit 6b3d82d, March 2026) still has no verified cleanup: CreateDesktopW per command, close result ignored, no retry. The shared-private-desktop patch in this thread (one desktop per process) is a validated direction - thousands of tool calls with zero DWM growth, 10h/4-agent run with DWM at 46 MB.
    • Please either ship the per-process desktop reuse, or add a cleanup that checks/retries CloseDesktop, or expose a supported toggle that stops per-call desktop creation without losing the sandbox isolation. Note that the existing windows.sandbox_private_desktop = false escape hatch does not preserve the isolation - it disables it entirely.

    Happy to provide a WPR/ETW trace or Process Monitor capture on request. This has been open since July 14 and is still affecting current builds - it's the difference between Codex being usable and not on Windows 10.

  8. deathy commented on Aug 10, 2026

    @deathy

    Adding DWM-side object counts that quantify this per tool call, plus where the CPU actually goes.

    Environment

    • Windows 11 Pro, build 26200 (25H2), x64, 24 logical cores, 64 GiB RAM
    • Codex/ChatGPT Store package OpenAI.Codex_26.803.5235.0_x64__2p2nqsd0c76g0
    • NVIDIA RTX 4070, driver 32.0.16.1088
    • Displays: 3840x2160 @ 60 Hz @ 150% DPI (primary) + 2x 1920x1080 @ 60 Hz @ 100%

    Method

    Rather than counting kernel handles, I dumped dwm.exe (full memory, MiniDumpWriteDump)
    and counted live composition objects by scanning for their vtable addresses:

    x dwmcore!CVisual::`vftable'
    s -q 0x10000 L?0x7ffffffeffff <vtable address>
    

    Tool-call counts come from ~/.codex/.sandbox/sandbox.<date>.log, counting
    setup refresh: spawning ...codex-windows-sandbox-setup.exe occurrences inside each
    measurement window.

    Both measurements below were taken against the same dwm.exe instance (PID unchanged),
    with Codex fully exited before the closing measurement in each case. No other
    application was started, closed, or resized during either window.

    Results: the leak is a constant per tool call

    Run 1 Run 2
    Wall-clock window 34 min 177 min
    Sandboxed command executions 395 359
    CVisual delta +2,393 +2,182
    CDesktopTreeData delta +2,395 +2,183
    CVisualOcclusionInfo delta +501 +391
    CVisual per tool call 6.06 6.08
    CDesktopTreeData per tool call 6.06 6.08

    The per-tool-call figure reproduces to within 0.3% across two runs whose durations
    differ by 5x. The leak tracks tool-call count, not session length or wall-clock time.
    CVisual and CDesktopTreeData stay locked at 1:1 throughout.

    None of it is released when Codex exits. Both closing measurements were taken with no
    ChatGPT.exe / codex.exe process running; the counts above are what remained.

    For scale, immediately after a dwm.exe restart this machine sits at 4,752 CVisual.
    Two Codex sessions took it to 9,487 - roughly doubling the entire desktop's composition
    tree from ~750 tool calls.

    Where the CPU goes

    Sampled profile of dwm.exe (xperf -on PROC_THREAD+LOADER+PROFILE -stackwalk profile,
    symbols from the public server) once the tree had grown large, showing why this presents
    as system-wide lag rather than a Codex-only problem:

    CComposition::ProcessComposition                            88.05%
    ├─ CComposition::PreRender                                  63.41%
    │   └─ CComposition::CleanTrees                             60.37%
    │       ├─ CPreWalkVisual::CalcTransform                    29.57%
    │       └─ CPreComputeContext::PreCompute                   12.97%
    └─ CRenderTargetManager::RenderAndPresent                   24.00%
        ├─ ComputeOverlayConfiguration (occlusion)              15.93%
        └─ CDDisplayRenderTarget::RenderAndPresent               7.97%
            ├─ ...DrawVisualTree                                 5.80%
            └─ Present                                           1.66%
    

    DWM walks the visual tree three times per frame plus a transform pre-walk, all O(visual
    count). Under 8% of its time is spent actually drawing or presenting - the rest is
    bookkeeping over the accumulated tree. On this machine that reached ~90% of one CPU core
    with the compositor otherwise idle, and restarting dwm.exe returned it to ~7%.

    Happy to re-run with additional counters if that would be useful.

  9. TobyIsADog commented on Aug 13, 2026

    @TobyIsADog

    Confirming that this is still happening on a newer Windows Store build.

    Environment

    • Codex 26.803.10989.0 (app-server 0.147.0-alpha.6.6)
    • Windows 11 Pro x64, build 26200
    • Intel Arc Graphics 32.0.101.6881 and NVIDIA RTX 4060 Laptop GPU
    • One 2560×1440 display, 32 GiB RAM

    What happens

    After Codex has been running tool-heavy tasks for many hours, the mouse pointer starts moving in visible steps across the desktop and other applications. The rest of Windows remains responsive. Signing out of Windows or rebooting restores smooth pointer movement, but the problem returns after another long Codex session.

    During one affected session, Codex and Windows had been running for about 28 hours. The sandbox log contained 9,688 launches of codex-windows-sandbox-setup.exe. At that point:

    • dwm.exe was using 749–766 MiB of private memory and had about 994 MiB of GPU memory committed.
    • The machine still had 16.1 GiB of available RAM and CPU usage was about 7%.
    • DPC and interrupt activity were low.
    • Windows had recorded no display-driver reset or Codex/GPU-process crash.

    This does not look like ordinary CPU or memory pressure, and it matches the DWM accumulation described in this issue.

    Why the private-desktop cleanup still looks relevant

    The current code still creates a fresh randomized CodexSandboxDesktop-* for a sandboxed process:

    Together with the repeatable per-tool-call CVisual and CDesktopTreeData growth reported above, this points to the same failure mode: private desktops are created repeatedly, some composition state is not released, and DWM gradually has more work to do each frame.

    A fix should keep the number of private desktops bounded or verify that desktop cleanup actually succeeds and that the related DWM objects return to baseline.

  10. roymasad commented on Aug 13, 2026

    @roymasad

    Same issue with win UI lag happening with me also with Codex left running for half a day.

    A force kill of dwm.exe is needed to restart it and get the UI back to a normal state.

  11. smallwangcf commented on Aug 14, 2026

    @smallwangcf

    Title: Windows-wide mouse/input stutter persists after opening a second Codex window until all ChatGPT.exe processes are terminated

    I am seeing intermittent system-wide mouse/input stutter on Windows while using Codex in the ChatGPT Desktop app. Chrome, File Explorer, and Codex are all affected. An unusual observation is that putting Task Manager in the foreground makes mouse movement feel smooth across the desktop, even when the Task Manager window is small.

    The clearest reproduction I have observed is:

    1. End all ChatGPT.exe processes in Task Manager. The system immediately becomes smooth.
    2. Open one Codex/ChatGPT Desktop window and run one Codex task. The system remains smooth.
    3. Open a second Codex/ChatGPT Desktop window. A second task is not required. Mouse stutter starts immediately.
    4. Close both visible ChatGPT/Codex windows. The stutter continues.
    5. End the whole remaining ChatGPT/Codex process group in Task Manager. The system immediately becomes smooth again.

    This does not appear to be reliably caused by running two tasks or by web preview itself. Opening the second window appears to trigger or expose a background app-process state that remains after the UI windows are closed.

    Read-only snapshots during the lag did not show general resource saturation: CPU was about 15% average (20% peak), DPC about 0.11% average, and interrupts about 0.63% average. The ChatGPT group had about 13 processes and about 3.19 GB private memory. A ChatGPT GPU process used about 10% average / 24% peak 3D, while dwm.exe used about 2.7% average / 3.9% peak. I saw no display-driver reset, DWM, or WHEA error around the second-window reproduction time.

    Hardware: 32 GB RAM, NVIDIA RTX 4060 + Intel UHD 770, 60 Hz display. Windows reports build 26200.8875. I also saw one earlier WER RADAR_PRE_LEAK_64 event for ChatGPT.exe, but I cannot prove that it is related.

    Could you please investigate whether the Windows desktop app can leave an Electron/Chromium renderer, GPU-process, DirectComposition, or DWM-composition state active after opening a second window or running Codex tools? This is a hypothesis based on the observed behavior, not a confirmed root cause.

    I can provide a sanitized diagnostic summary. I have not captured an ETW trace yet because Windows Performance Recorder requires an elevated administrator session.

  12. SayrWolfridge commented on Aug 15, 2026

    @SayrWolfridge

    Additional sanitized evidence for #33192

    No raw ETL, CSV export, screenshot, process tree, command line, or machine-specific path is attached. All times below are relative to the retained trace interval.

    Reproduction on a newer build

    • Codex package: 26.810.4967.0
    • Command runner: codex-command-runner-0.148.0-alpha.9.exe
    • Controlled test: eight minimal terminal invocations followed by one sampling invocation
    • Persistent DWM Composition change: +18, exactly +2 per runner invocation

    This reproduces the issue on a newer package than the one in the original report.

    ETW handle-lifetime evidence

    A separate retained ETW slice from package 26.803.10989.0 and command runner 0.147.0-alpha.6.6 contained:

    • 18 DWM Composition creation bursts
    • 111 newly observed Composition objects
    • 81 objects closed within the retained slice
    • 30 objects still outstanding at the end of the slice
    • 14 of 18 creation bursts matched command-runner WindowUpdate events within 50 ms
    • All 126 command-runner WindowUpdate events fell inside those matched burst windows
    • Those 14 runner-associated bursts accounted for 28 of the 30 newly outstanding objects
    • Each matched runner burst created seven Composition objects, closed five, and left two outstanding

    The observed enrichment of runner WindowUpdate events around DWM creation bursts was approximately 80x over a uniform-background expectation at a 50 ms window.

    Symbolized creation path

    The repeated DWM-side creation path was:

    dwmredir.dll!CPortBase::PortThread
    dwmredir.dll!CSessionPort::ProcessCommand
    uDWM.dll!CWindowList::ProcessAsyncDwmMessage
    uDWM.dll!CWindowList::DesktopCreate
    uDWM.dll!CWindowList::GetGlobalLightSetForDesktop
    uDWM.dll!CGlobalLightSet::Create
    uDWM.dll!CGlobalLightSet::Initialize
    uDWM.dll!CreateCoordinateSpaceVisual / AddNewSharedDistantLightWithType
    uDWM.dll!UdwmDcompVisual::CreateSharedVisual
    dcomp.dll!Windows::UI::Composition::InteropCompositor::Api::OpenSharedResourceHandle
    dcomp.dll!CompositionObject::CreateHandle
    

    Interpretation

    The evidence points to per-command Windows sandbox runner lifecycle as the trigger. Each short-lived runner produces a Win32k window update that is followed within milliseconds by DWM desktop/global-light-set creation, with two Composition objects remaining outstanding after the runner exits.

    The trace does not establish whether the missing release belongs in the runner, console/window lifecycle, DirectComposition interop, or DWM itself. It does narrow the trigger from the general Codex UI to the per-command Windows runner path.

  13. GaleVale commented on Aug 23, 2026

    @GaleVale

    Adding sanitized evidence from the current Store package. This confirms that the system-wide DWM bottleneck is still present on a build newer than the ones above, while also showing that a short total-handle test does not reproduce the lifetime signature every time.

    Environment

    • Codex/ChatGPT Store package: OpenAI.Codex_26.818.5229.0_x64
    • Chromium/FileVersion: 151.0.7922.170
    • Windows 11 x64, build 26200, 24 logical processors, 96 GiB RAM
    • Intel Core Ultra 9 285K; NVIDIA RTX 5090, driver 32.0.16.1088
    • Dell S2425HSM, 1920x1080; reproduced at 143.85 Hz and still observable at 60 Hz
    • HAGS enabled (HwSchMode=2); no OverlayTestMode MPO override

    The symptom is Windows-wide window-drag/input stutter, not merely a slow Codex window. It persisted with substantial CPU/RAM headroom and low GPU, disk, DPC, and interrupt activity. It also reproduced at 144 Hz after the emulator device process had exited, so the emulator is not a necessary condition.

    WPR/ETW evidence on 26.818.5229.0

    I captured the affected drag interval with the Microsoft CPU.Verbose + DesktopComposition.Light WPR profiles, then decoded it with WPA/Xperf. The analysis interval was restricted to the actual 21.5-second drag action (0.5-22.0 s), excluding trace-save work.

    • The system retained 85.93% of total CPU capacity as Idle.
    • dwm.exe averaged 0.99 logical core.
    • One DWM frame thread accounted for 85.50% of DWM sampled CPU; 87.10% of that thread's exclusive samples were in dwmcore.dll.
    • DWM Ready time averaged 8.73 us; sustained scheduler starvation was not present.
    • NVIDIA user-mode/kernel modules were only 1.96% / 0.57% of that DWM frame thread. GPU work was not saturated, and no third-party display/overlay module appeared in the hot path.
    • The three principal Codex ChatGPT.exe processes together averaged about 0.56 logical core; no single Codex process saturated a core.

    An earlier lossless DesktopComposition.Light capture over an 18.482-second DWM root region showed:

    • ProcessFrame: 14.097 s
    • Present: 7.477 s
    • Occlusion: 4.169 s
    • PreCompute: 2.935 s
    • VSyncDPC: 142.35/s, matching the 143.85 Hz display mode

    This independently matches the DWM-side CPU shape reported above: the user-visible failure is a serial composition/frame-thread bottleneck while the rest of the machine remains mostly idle.

    Mitigation A/B

    Disabling “Show window contents while dragging” at 144 Hz reduced:

    • DWM average CPU: 169.67% -> 76.93% on a one-core-equals-100% scale (-54.7%)
    • Codex/ChatGPT CPU: 45.21% -> 16.35% (-63.8%)
    • Context switches: 190,638/s -> 43,261/s (-77.3%)

    This is a useful workaround, not a fix: DWM still remained around 0.77 core, and the user must give up live window contents. Reducing the display to 60 Hz also improves usability but does not eliminate the DWM bottleneck.

    Short handle control on the current build

    I also ran two controlled five-terminal-call batches while sampling DWM's total handle count:

    • Stable 20-second baseline: 2392 -> 2391 (range 2391-2394)
    • The first batch produced a transient rise (up to about +45 versus baseline), followed by recovery toward 2395
    • During a second 75-second time-series, the five calls produced no persistent step; the count fell from 2424 to 2395 and remained there through the end
    • DWM working set/private memory stayed near 811 / 674 MiB during this short test

    This test did not enumerate the Composition handle type, so it does not disprove the typed per-runner leak documented in the comments above. It does show that the short-run total-handle signature is state-dependent on 26.818.5229.0, while the DWM frame-thread performance failure remains reproducible.

    Request

    Please keep investigating this against the current Windows desktop package rather than treating it as limited to the older builds. I retained the original 1.71 GiB CPU+composition ETL and sanitized WPA/Xperf exports locally. I have not attached the raw ETL because it contains a wider process timeline. If maintainers provide a privacy-safe upload route and specify the desired provider/keyword set, I can supply the trace or repeat a targeted capture.

  14. MangelSpec commented on Aug 27, 2026

    @MangelSpec

    How is this issue not considered a very high priority yet? I installed the app for the first time and genuinely thought something was wrong with my PC because the entire Windows desktop became increasingly unusable after only a few hours, even when closing every software (including GPT).
    After finding this thread, I’m pretty shocked to see reports going back this far without any visible indication that the issue has been elevated.
    In my case, restarting dwm.exe immediately fixes the system-wide lag, while Codex can remain open. CPU, GPU and memory usage are all low. The apparent handle/composition-resource leak also means the problem is accumulating long before the desktop becomes noticeably unusable.
    I also asked several friends who use the Windows app, and every single one of them confirmed experiencing the same degradation.
    This makes the Windows app effectively unusable for me and seriously makes me consider uninstalling it again. Is this being actively investigated / treated as a high-priority issue?

  15. chess-oai commented on Aug 28, 2026

    @chess-oai
    Contributor

    ChatGPT v26.825.32147 includes a fix for one cause of Windows stuttering and slowdowns during long sessions. Sandboxed commands could cause Desktop Window Manager (dwm.exe) resources to accumulate. The fix reuses private Windows desktops for commands with the same permissions.

    Please update to ChatGPT v26.825.32147 or later, restart the app, and let us know whether this helps.

    If the problem continues after updating, please share your version and steps to reproduce.

    Thank you for your reports and investigation!

  16. Hajmer777 commented on Aug 29, 2026

    @Hajmer777

    Additional sanitized evidence for #33192

    No raw ETL, CSV export, screenshot, process tree, command line, or machine-specific path is attached. All times below are relative to the retained trace interval.

    Reproduction on a newer build

    • Codex package: 26.810.4967.0
    • Command runner: codex-command-runner-0.148.0-alpha.9.exe
    • Controlled test: eight minimal terminal invocations followed by one sampling invocation
    • Persistent DWM Composition change: +18, exactly +2 per runner invocation

    This reproduces the issue on a newer package than the one in the original report.

    ETW handle-lifetime evidence

    A separate retained ETW slice from package 26.803.10989.0 and command runner 0.147.0-alpha.6.6 contained:

    • 18 DWM Composition creation bursts
    • 111 newly observed Composition objects
    • 81 objects closed within the retained slice
    • 30 objects still outstanding at the end of the slice
    • 14 of 18 creation bursts matched command-runner WindowUpdate events within 50 ms
    • All 126 command-runner WindowUpdate events fell inside those matched burst windows
    • Those 14 runner-associated bursts accounted for 28 of the 30 newly outstanding objects
    • Each matched runner burst created seven Composition objects, closed five, and left two outstanding

    The observed enrichment of runner WindowUpdate events around DWM creation bursts was approximately 80x over a uniform-background expectation at a 50 ms window.

    Symbolized creation path

    The repeated DWM-side creation path was:

    dwmredir.dll!CPortBase::PortThread
    dwmredir.dll!CSessionPort::ProcessCommand
    uDWM.dll!CWindowList::ProcessAsyncDwmMessage
    uDWM.dll!CWindowList::DesktopCreate
    uDWM.dll!CWindowList::GetGlobalLightSetForDesktop
    uDWM.dll!CGlobalLightSet::Create
    uDWM.dll!CGlobalLightSet::Initialize
    uDWM.dll!CreateCoordinateSpaceVisual / AddNewSharedDistantLightWithType
    uDWM.dll!UdwmDcompVisual::CreateSharedVisual
    dcomp.dll!Windows::UI::Composition::InteropCompositor::Api::OpenSharedResourceHandle
    dcomp.dll!CompositionObject::CreateHandle
    

    Interpretation

    The evidence points to per-command Windows sandbox runner lifecycle as the trigger. Each short-lived runner produces a Win32k window update that is followed within milliseconds by DWM desktop/global-light-set creation, with two Composition objects remaining outstanding after the runner exits.

    The trace does not establish whether the missing release belongs in the runner, console/window lifecycle, DirectComposition interop, or DWM itself. It does narrow the trigger from the general Codex UI to the per-command Windows runner path.

    [Windows 10][Codex 26.825.4187.0] DWM handles grow to ~28K and cause severe system-wide window-drag lag

    Codex version

    Microsoft Store/MSIX package: OpenAI.Codex 26.825.4187.0
    Embedded Chromium: 151.0.7922.174

    System

    • Windows 10 Pro 22H2, build 19045.6466
    • Intel Core i5-13400F, 10 cores / 16 logical processors
    • NVIDIA GeForce RTX 4070
    • NVIDIA driver: 32.0.15.8142
    • MSI G272QPF E2, DisplayPort, 2560x1440 at 180 Hz

    Problem

    After extended Codex Desktop usage, dragging any Windows application becomes extremely delayed. The dragged window can lag many seconds behind the mouse pointer, while the rest of the system remains fast.

    Closing Codex or restarting Explorer does not repair the already degraded desktop compositor.

    Measurements while affected

    • dwm.exe: 28,202 handles
    • dwm.exe private memory: 566 MB
    • During window dragging:
      • DWM average CPU: 10.61% of the whole 16-thread CPU
      • DWM peak CPU: 11.8%
      • DWM peak GPU engine usage: 22%
      • explorer.exe peak CPU: approximately 0.1%
    • No NVIDIA display-driver reset, WHEA, or GPU hardware errors were found.

    The current Codex profile also recorded 86 occurrences of:

    [desktop-notifications][global-error] ResizeObserver loop completed with undelivered notifications

    within approximately three minutes.

    Recovery test

    I force-restarted only dwm.exe as SYSTEM.

    Before:

    • PID 1240
    • 28,202 handles
    • 566 MB private memory

    After:

    • New PID 37436
    • 1,536 handles
    • 118 MB private memory

    Window dragging became immediately smooth again without restarting Windows.

    Codex itself did not recover from the DWM restart and had to be terminated through Task Manager and relaunched.

    Reproduction pattern

    1. Restart DWM or start a fresh Windows session.
    2. Use Codex Desktop normally for extended coding sessions with tool calls.
    3. Periodically check the DWM handle count.
    4. Eventually, DWM accumulates tens of thousands of handles.
    5. Dragging unrelated application windows becomes severely delayed.
    6. Restarting DWM immediately clears the problem.

    Expected behavior

    Codex tasks, renderer updates, notifications, tool calls, and process cleanup must release their DirectComposition/DWM resources. Long Codex sessions should not progressively degrade the Windows desktop compositor.

    Related issues

    This is still reproducible on Codex 26.825.4187.0.

  17. codengine commented on Aug 30, 2026

    @codengine

    ChatGPT v26.825.32147 includes a fix for one cause of Windows stuttering and slowdowns during long sessions. Sandboxed commands could cause Desktop Window Manager (dwm.exe) resources to accumulate. The fix reuses private Windows desktops for commands with the same permissions.

    Please update to ChatGPT v26.825.32147 or later, restart the app, and let us know whether this helps.

    If the problem continues after updating, please share your version and steps to reproduce.

    Thank you for your reports and investigation!

    Thanks for working on this. I tested and inspected the fix from #40672, including its integration into 0.151.0. It fixes an important part of the problem, but not the complete failure mode I observed.

    The PR fixes private-desktop lifetime and reuse: the parent retains desktops and reuses them only when the sandbox account and effective policy match. That removes repeated private-desktop creation and is the right behavior, especially for TTY/ConPTY commands.

    However, those desktops still live under the interactive Winsta0 window station. In 0.151.0:

    • codex-command-runner is still a console-subsystem executable;
    • ordinary non-TTY commands use ConsoleMode::Inherit—only the filesystem helper uses NoWindow;
    • both reused private desktops and the default desktop are under Winsta0.

    The traces indicate that desktop lifetime alone was not the root issue. The remaining trigger was the repeated creation of console/window composition resources inside the interactive window station. DWM retained DirectComposition resources after the short-lived command process tree exited. Over a long session, DWM memory/handles and frame-thread work accumulated until dragging windows and system-wide input became sluggish.

    In a controlled ETW test, 12 command executions with the shared/reused Winsta0 desktop approach produced 144 DirectComposition resource-creation events and no corresponding releases. Therefore, pooling the desktop reduced churn but did not remove DWM from the command lifecycle.

    The fix that has remained stable for me does the following:

    1. Builds codex-command-runner as a Windows GUI-subsystem executable.
    2. Uses CREATE_NO_WINDOW for non-TTY commands and setup helpers.
    3. For elevated non-TTY commands, creates a private desktop in a separate noninteractive window station, not under Winsta0.
    4. Passes that station\desktop through STARTUPINFO.lpDesktop and keeps both handles alive for the child lifetime.
    5. Restricts the station/desktop DACL to the sandbox user and logon SIDs.
    6. Keeps the upstream policy-aware reusable Winsta0 desktop for TTY/ConPTY commands.
    7. Uses STARTF_FORCEOFFFEEDBACK for the runner to avoid the unrelated busy-cursor feedback on every command.

    With that topology, the same ETW control produced zero of the problematic DirectComposition creation events, with zero lost ETW events. After approximately one week of normal use, DWM memory has remained low and I have seen no recurrence of the drag/input lag.

    So my conclusion is:

    • Reuse private desktops across Windows sandbox commands #40672 correctly fixes private-desktop ownership, lifetime, and policy-safe reuse.
    • It does not completely fix the DWM degradation because non-TTY command consoles can still participate in composition through the interactive window station.
    • The decisive change is isolating non-TTY command execution from Winsta0, not pooling those command desktops.

    The 0.151.0-based implementation I am currently using is here:

    https://github.com/codengine/codex/tree/local/fix-dwm-degredation

    The noninteractive station implementation is here:

    https://github.com/codengine/codex/blob/local/fix-dwm-degredation/codex-rs/windows-sandbox-rs/src/desktop/noninteractive.rs

    Would you consider routing elevated non-TTY commands through a noninteractive window station upstream while retaining the new pooled desktop path for TTY commands? I can also repeat a targeted ETW capture or provide more detailed event data if useful.

  18. TheThane69 commented on Aug 30, 2026

    @TheThane69

    I packaged a guarded workaround for this recovery path:

    https://github.com/TheThane69/ChatGPT-DWM-Memory-Leak-Workaround

    It validates the active session and exact dwm.exe identity, records DWM/display
    state, recreates only the affected DWM process, reloads the relevant Windows
    display components, and verifies the final display state.

    Observed result on the original affected session:

    • DWM private memory: 962.8 MB -> 106.8 MB
    • Handles: 3,561 -> 2,345
    • Windows UI responsiveness restored without rebooting

    It is an unofficial workaround, not a root-cause fix, and its physical-console
    path currently has intentionally strict compatibility checks.

  19. acumina commented on Aug 31, 2026

    @acumina

    Adding another Windows 11 reproduction and a successful workaround A/B.

    Original affected build

    • Codex MSIX: OpenAI.Codex_26.820.10647.0_x64
    • 240 Hz display; no G-SYNC support
    • HAGS enabled; MPO already disabled

    Symptoms

    After several days of tool-heavy Codex use, the whole Windows desktop became increasingly sluggish. Dragging Windows Settings was worst, Codex was intermediate, and classic Control Panel was least affected. At 60 Hz the visible stutter was reduced, but windows followed the pointer with a long delay. At 240 Hz the stutter was more obvious.

    Fully exiting ChatGPT/Codex did not restore responsiveness. Restarting Explorer, resetting the graphics driver, changing refresh rate, disabling transparency, using one window, disconnecting the mouse, and lowering mouse polling rate did not fix it. Signing out of Windows and signing back in immediately restored normal behavior at both 60 Hz and 240 Hz.

    During the affected session:

    • DWM private memory: 502.6 MB
    • DWM working set: 437.6 MB
    • Total GPU memory used: approximately 1386 MiB

    Immediately after signing back in:

    • DWM private memory: 218.1 MB
    • DWM working set: 105.5 MB
    • Total GPU memory used: approximately 720 MiB

    Two lossless ~21-second GPU/DWM traces also showed a notable change in AllocationFault Reason 1: approximately 31.7/s and 46.8% of allocation faults in the bad state, versus 11.1/s and 8.9% after signing back in.

    Workaround tested

    Changing only windows.sandbox = "elevated" to "unelevated" did not avoid the private-desktop path. I then backed up ~/.codex/config.toml and used:

    [windows]
    sandbox = "unelevated"
    sandbox_private_desktop = false

    After restarting the app, a Win32 desktop probe confirmed that new tool processes changed from a randomized CodexSandboxDesktop-* desktop to Default.

    The workaround removes private-desktop UI isolation, but retains the native file/permission sandbox. It is therefore a compatibility workaround rather than an ideal security-preserving fix.

    Result after about three days

    The system-wide dragging/input lag has not returned. At 240 Hz, Windows Settings and Codex remain responsive.

    A controlled run of 20 separate minimal terminal calls produced no immediate DWM growth:

    • Before: 420.6 MB private, 2141 handles, 121.5 MB working set
    • After settling: 420.7 MB private, 2137 handles, 121.5 MB working set

    There were no new relevant Application or System events, and all ChatGPT/Codex processes remained responsive.

    The app subsequently auto-updated to MSIX 26.825.5331.0, so I cannot cleanly separate the effect of sandbox_private_desktop = false from newer changes such as #40672. However, disabling the per-command private-desktop path has been effective so far, and the previously observed per-tool-call resource increase did not reproduce.

    No raw ETL, account data, local paths, API credentials, or complete logs are included.

  20. TobyIsADog commented on Sep 5, 2026

    @TobyIsADog

    @chess-oai Cursor stutter persists on Codex app 26.901.5280.0 (codex-cli 0.153.4) despite #40672.

    On my machine, cursor delay and stutter consistently build up whenever Codex runs for extended periods. It starts with noticeable lag during fast pointer movement and progresses until the pointer is almost unusable across Windows. Restarting Codex does not clear the accumulated stutter, even while the rest of the system remains responsive.

    Local ETW analysis identifies DWM input hit-testing blocked on composition-tree locks. During continuous pointer movement, all 508 ProcessQueuedMouseEvents calls lasting at least 8 ms contained these waits, accounting for 98.78% of their combined duration:

    • CInputManager::s_HitTestRequest → CTreeLock::AcquireShared, with the composition thread readying the input thread in CComposition::PreRender.
    • CInputManager::s_HitTestRequest → CComposition::GetDesktopTree, with the composition thread readying the input thread in CComposition::CleanTrees.

    The longest call was 13.275 ms. A previous capture during more severe stuttering showed the same pattern in all 211 calls lasting at least 16 ms, reaching 25.51 ms. The immediate input delay is dominated by these lock waits.

    A desktop-reuse gap remains in rust-v0.153.4: each sandboxed filesystem request spawns a new child through the Windows sandbox wrapper. The desktop cache is process-local, and the wrapper exits after each request, preventing reuse across requests.

    In four otherwise identical read-only FS requests, both private-desktop ON runs created a new Codex desktop and the same 12-resource DirectComposition cluster; both OFF runs created neither. This establishes repeated resource creation on the helper path. Whether those resources persist and cause the later lock contention remains unresolved.

    Please check whether private desktops can be reused across FS-helper processes, and whether their repeated creation contributes to DWM composition-tree lock contention.

  21. Cielluloid commented on Sep 9, 2026

    @Cielluloid

    Building on TobyIsADog’s FS-helper analysis, I’m adding evidence that DWM table entries remain after the kernel Desktop objects have been deleted.

    These measurements were captured on September 9, 2026 (UTC+08:00), on Windows 10 x64, build family 19045, with a Codex backend/command runner 0.153.4. The tested path used the elevated Windows sandbox with private desktops enabled.

    File-operation A/B

    A passive ETW trace recorded process lifetimes and Desktop ObjectCreate/ObjectDelete events. On disposable text files, the observed sequence was:

    Action Desktop creates Desktop deletes
    Ordinary shell write 0 0
    apply_patch: add file 3 3
    apply_patch: update file 4 4
    apply_patch: add another file 3 3

    All 10 creators belonged to this process chain:

    persistent app-server
      → short-lived Windows sandbox wrapper
        → command runner
          → filesystem helper
    

    All 10 kernel Desktop objects were subsequently deleted. Lifecycles were counted using create/delete intervals, accounting for reused kernel addresses. The 3/4/3 counts describe this capture, not a fixed number for every patch.

    Across the same DWM process lifetime, its desktop/GlobalLight table grew from 13,343 to 13,353, with all previous entries preserved. Earlier controlled empty-Desktop lifecycle tests independently produced one retained table entry per cycle.

    This establishes completed kernel Desktop lifecycles alongside persistent DWM table growth. It does not provide a one-to-one mapping between every ETW object and every DWM desktop ID.

    Composition identity comparison found 24 added, 2 removed, net +22. The additional net 2 beyond the earlier two-per-Desktop pattern remain unassigned.

    Performance and recovery

    On this machine, 98.45% of samples on the saturated DWM scene-update thread were in CWindowList::UpdateScene. Local code/object inspection connected its traversal to the desktop/GlobalLight table and coordinate-visual reference counting. This is a measured drag bottleneck; it does not establish the cause of the hit-testing lock waits reported on another machine.

    One separately authorized DWM replacement reduced:

    • Desktop/GlobalLight entries: 13,551 → 9
    • Composition handles: 27,819 → 503
    • Average drag CPU: 1.37677 → 0.35897 logical cores, a 73.93% reduction, with 13/13 valid samples in each phase

    Window dragging became smooth. The Codex UI subsequently hung and required relaunch; Application Hang events corroborated that side effect. DWM replacement resets other state too and does not validate a permanent client fix.

  22. chess-oai commented on Sep 18, 2026

    @chess-oai
    Contributor

    we’ve shipped a follow-up fix in ChatGPT 26.915.31945 (Microsoft Store package 26.915.4065.0) that addresses the filesystem-helper behavior identified in this thread. helpers now reuse a desktop retained by the longer-running process instead of creating a fresh one for each operation.

  23. KubaO commented on Oct 16, 2026

    @KubaO

    Lol, I wish I found this one earlier. I've just had to diagnose the same issue, except it was my own test runner (nothing to do with Codex) that was creating private desktops, thousands of them. Same symptoms, same cause: DWM leaks handles - not desktop handles, but compositor handles that it creates when a desktop is created.

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 workingperformancewindows-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