Memory/CPU leak in "Cursor Agents" window renderer after long-running sessions (multi-day)

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

Symptom:
After leaving Cursor open for 1–2 days, the app gradually consumes all available CPU and memory, to the point of degrading the whole machine (swap nearly maxed, system load average >25 on a 12-core Mac). This is the second time I’ve hit this.

Diagnosis via Cursor’s own Process Explorer (Help → Open Process Explorer):

The load isolates to a single process: window [1] (Cursor Agents) — the Agents panel, not a regular editor window or extension host.
CPU: that one process accounted for 636.5% (90.4% of all CPU used by Cursor) sustained over hours, while every other component (main process, extension hosts, MCP processes) combined stayed under 10%.
Memory: same process held 2386 MB (60.8% of total Cursor memory) at peak, vs. 100–190 MB for each per-workspace extension host.
Memory history graph: a normal sawtooth pattern (rise → GC → drop) overnight, then a step-jump to a permanently higher floor (~3–4 GB) the next morning that never returns to baseline — consistent with retained memory across GC cycles rather than active workload.
No active network connections were held by the process (checked via lsof -i) at the time of the spike, and no agent appeared to be actively producing new output — CPU stayed pegged with no corresponding activity, which points to a render/event loop issue rather than genuine background-agent computation.

Repro pattern: open Cursor, use the Agents/background-agents panel across a normal workday, leave the app open overnight/multi-day without restarting. CPU and memory climb and don’t recover until the app (or at least that window) is restarted.

Ask: would help to know if others see the same isolation to the Agents window specifically, and whether there’s a known leak in agent transcript/session retention in that panel.

Steps to Reproduce

Repro pattern: open Cursor, use the Agents/background-agents panel across a normal workday, leave the app open overnight/multi-day without restarting. CPU and memory climb and don’t recover until the app (or at least that window) is restarted.

Operating System

MacOS

Version Information

Version: 3.16.17
VS Code Extension API: 1.128.0
Commit: 6b2afae0257df2bb5e1835f15165dc2f0de056b0
Date: 2026-08-14T01:41:12.803Z
Layout: Agent Window
Build Type: Stable
Release Track: Default
Electron: 40.10.3
Chromium: 144.0.7559.236
Node.js: 24.15.0
V8: 14.4.258.32-electron.0
xterm.js: 6.1.0-beta.291
OS: Darwin arm64 25.5.0

Additional Information

it makes cursor so slow it is effectively unusable

Does this stop you from using Cursor

Yes - Cursor is unusable

Thanks for the detailed write-up. Isolating it to the Cursor Agents window in Process Explorer, with CPU staying high while nothing is producing output, is really helpful.

What you are seeing is not caused by anything in your setup. This is an issue we are tracking with the Agents window renderer after a long session left open. No timeline to share yet, but I will post here when there is an update.

Until then, the reliable way to reclaim that process is:

  1. Command Palette (Cmd+Shift+P) > Developer: Reload Window on the Agents window. That resets the renderer. It will also stop any local agents still running in that window, so finish or pause those first if you can.
  2. If Reload Window does not respond because the window is already too wedged, fully quit with Cmd+Q and reopen.

Let me know if you have any follow-up questions!

This is affecting me as well. Cursor renderer at 650% CPU on Mac OS.

Additional data point — Windows, Glass mode, agent-loop renderer OOM (Aug 26, 2026)

Cross-ref from closed threads #154212 (listener leak / AgentExecFileChange) and #156475 (agent-loop OOM). Same crash class, but with new evidence on Glass workbench churn.

Environment

  • OS: Windows 11 (10.0.26200)

  • Cursor: 3.17.21 (stable), Glass / Agents layout

  • Workspace: single local folder, not a git repo

  • System RAM: 32 GB (~17 GB free at investigation — not system OOM)

Crash

2026-08-26 23:30:09.620 [error] CodeWindow: renderer process gone (reason: oom, code: -536870904)

6 extension hosts exited immediately after. Cursor auto-recovered ~14s later.

Timeline

  • Session started 11:49, ~12 hours without restart

  • 23:28:38 — agent-loop wakelock acquired

  • 23:30:09 — renderer OOM (~90s into agent-loop)

Listener leak (matches #154212)
16× potential listener LEAK detected in main renderer (window1_wb0). Stack:

$onAgentExecFileChange → _handleRegularFileChange → createTextEditorModel → listener LEAK

Last 3 warnings within 8 minutes of crash.

Glass-specific amplifier (new data point)

  • 166 workbench instances spawned in one window over the session (window1_wb0window1_wb165)

  • 160 [GlassMain] Deregistered events — child workbenches tear down loggers, but parent renderer wb0 persisted 12h and accumulated state

Ruled out

  • Large chat context (conversationLength: 1723 on resume)

  • Disk tool cache (~24 MB on disk, not in renderer heap)

Workarounds in use: restart every few hours, close stale agent tabs, .cursorignore for noisy dirs.

Happy to attach log excerpts or Process Explorer screenshots. Does the team have a preferred open thread for Windows Glass-mode OOM, or should I file a new bug report?

Cross-ref: #154212, #156475, #158856, GitHub #3931

Still present in 3.20.0 on Windows 11. I checked the 3.20.0 bundles against what we traced on 3.19.14 and 3.19.16. The three code paths below are unchanged, so this build did not fix the Agents window problem. All evidence is inline.

Environment

OS: Windows 11 (10.0.26200)
Cursor: 3.20.0 stable, commit 4c0fe9acf024d051c47ffe28c48918ef6835f370, built 2026-09-07T23:10:23Z, Agents / Glass layout
VS Code Extension API: 1.128.0
Machine: Threadripper 3990X (64 cores), 256 GB RAM. Usually 20 or more agent chats open in the Agents window.

  1. Renderer memory: every persisted inline diff is restored eagerly at window start

Kevin wrote on thread 170166 on Sep 1: “Pending changes are restored into the reloaded window, which contributes to it coming back at 1.6 GB.” That restore is the memory floor on this machine.

Sep 6 (3.19.14), Agents window, DevTools heap snapshot with no chat open: 517 TextFileEditorModel instances, all file: URIs, for files no editor in that window had open. Every one was held through the modelRef of an inline diff handler, 522 handlers in the diffHandlers map of the InlineDiffService singleton. Renderer heap was 471 MB with no chat open. renderer.log one second after the workspace loaded: Potential working copy LEAK detected, having 513 working copies.

Where it comes from: InlineDiffService.loadInlineDiffs() reads every persisted diff for the workspace out of state.vscdb (cursorDiskKV rows inlineDiff:<workspaceId>:<diffId>, the review state of every agent edit nobody clicked Keep or Undo on; 1,258 rows for one workspace on Sep 6, and nothing prunes them). It then checks the client feature gate park_inline_diffs. In 3.20.0 that gate is still park_inline_diffs:{client:!0,default:!1}, so the default path is restoreAllStoredDiffsEagerly: a live model reference, text model and listeners for every distinct file, kept for the life of the window. Diffs with visible decorations also set the file back to its original text, re-apply the edit and force a save (77 files rewritten on disk at every window start here).

The gate ON path already does the right thing: pruneStoredDiffs keeps the 128 newest, restores only diffs whose file already has an open model, parks the rest as metadata and restores one when its file is opened. I forced that gate on locally on Sep 6. The Potential working copy LEAK line has not appeared since, zero in the 3.19.16 and 3.20.0 session logs. Please default park_inline_diffs to true, or turn it on for everyone.

  1. Renderer listener leak on $onAgentExecFileChange, still firing in 3.20.0

Two events in this 3.20.0 session so far, at 2026-09-08 01:26:32 and 2026-09-08 02:11:37, same stack both times. renderer.log, trimmed to the relevant frames:

2026-09-08 01:26:32.097 [error] potential listener LEAK detected, popular: Error
    at V6v.create (workbench.glass.main.js:53:14063)
    at hTs._event [as onDidChange] (workbench.glass.main.js:55:2733)
    at new JO (workbench.glass.main.js:228:34972)
    at aKl._createModelData (workbench.glass.main.js:20761:2522)
    at aKl.createModel (workbench.glass.main.js:20762:1283)
    at m9e.doCreateTextEditorModel (workbench.glass.main.js:11928:36329)
    at m9e.resolveFromFile (workbench.glass.main.js:20740:17736)
    at async nKl.createModelReference (workbench.glass.main.js:20740:32518)
    at async jXl.maybeHoldGlassLspTextModelReference (workbench.glass.main.js:20970:2187)
    at async jXl._handleFileChangeImpl (workbench.glass.main.js:20970:5571)
    at async C.provider.createRemoteAccessor.composerId [as fileChangeHandler] (workbench.glass.main.js:20971:34853)
    at async aEs.$onAgentExecFileChange (workbench.glass.main.js:20566:571808)

Each agent file change event resolves a text model in the Agents window renderer through maybeHoldGlassLspTextModelReference. The models are retained, and the shared onDidChange emitter they subscribe to accumulates listeners until the detector fires. Same entry point as the Aug 26 post in this thread on 3.17.21. The park_inline_diffs gate does not touch this path. This is the one I have no workaround for.

  1. Extension host: the agent store sync lists every mounted store every 5 seconds and launches whoami.exe four times a second per chat

One cloud agent store is mounted per live chat, plus the user’s own store. In cursor-agent-exec/dist/main.js (the bundled agent store sync engine) the periodic full sync round is scheduled syncDebounceMs after the previous round ends, whether or not anything changed. In 3.20.0 the defaults object still reads syncDebounceMs:5e3 and the clamp is still syncDebounceMs:{min:250,max:36e5}. On top of that, forceSync() runs an on-demand full round for every caller that wants the store fresh, so each chat event lists every mounted store in lockstep. Measured on 3.19.14 (Sep 6, one hour, 12 mounts): 8,072 aiserver.v1.BackgroundComposerService.ListAgentStoreDirectory calls, 2.24 per second sustained, bursts of 12 per second, while every store’s files/ directory was empty. The calls are logged in the window’s exthost/anysphere.cursor-always-local/Cursor Structured Logs.*.log as "metadata":{"method":"ListAgentStoreDirectory"}.

The lock check is the expensive part. The same engine runs setInterval(()=>{this.checkExclusiveMutationClaim()},Nn) with Nn=250 per mounted store, and the identity lookup inside it is execFileSync("whoami",["/user","/fo","csv","/nh"]), a synchronous process launch on the extension host’s only thread, recomputed on every tick. That is about 4 whoami.exe launches per second per live chat. A CPU profile of the extension host on 3.19.16 (Sep 7) spent 97% of a 20 second sample inside spawnSync on that call. On Sep 6 with only 4 chats open, tool calls in the Agents window ran at an 8.8 s median (Read 2.0 s, Write 9.2 s, shell 10.7 s) on an otherwise idle 64 core machine, because every tool call queues behind those spawns. Both literals are unchanged in 3.20.0.

Local experiment on Sep 6 and 7: periodic round at 60 seconds instead of 5, on-demand full rounds skipped, and the whoami result cached for the life of the extension host. The Agents window tool call median went from 8.8 s to 10 to 50 ms and has stayed there through the 3.20.0 update (2,625 tool calls across 22 chats in the last hour). So those two paths are the tool latency, and nothing on this machine writes to those stores from elsewhere, so a 5 second listing timer does no work here.

Asks: default park_inline_diffs to true, cache the whoami SID once per extension host, and let the periodic store round back off when the previous round found nothing. Item 2 is the remaining leak on your side.

Workaround for items 1 and 3 until a fix ships. Windows only, unofficial, unsupported, use at your own risk.

These are the edits behind the numbers in my report above, minus one that I do not recommend for everyone (see the end). They change two minified files in the Cursor install folder. Cursor puts the stock files back on every update, so the edits are lost on update and have to be re-applied. The script below saves a copy of each stock file as .orig before it writes; to undo, copy the .orig back over the file. Close Cursor completely before running it (check Task Manager for a leftover Cursor.exe) and start it again afterwards. Item 2, the listener leak, has no workaround that I know of.

Edit 1 (memory floor). File out\vs\workbench\workbench.glass.main.js, the Agents window bundle. The string this._parkInlineDiffs=this.experimentService.checkFeatureGate("park_inline_diffs" occurs once in 3.20.0. Insert !0|| after the =; the gate is then on regardless of what the experiment service returns. This file is not in the checksums list in product.json, so Cursor does not show the “installation appears to be corrupt” notice or the [Unsupported] title. The classic editor window reads the same code from workbench.desktop.main.js, which is checksummed, so I leave that file alone here.

Edit 2 (tool latency). File extensions\cursor-agent-exec\dist\main.js. In 3.20.0 the lock identity function starts with function Tt(){const e=String((0,Ye.execFileSync)("whoami",["/user","/fo","csv","/nh"],{encoding:"utf8"})).trim() and ends with return e}. Tt, Ye and e are minified names and can change between builds, which is why the script finds them with a pattern instead of a fixed string. The edit adds if(void 0!==Tt.__cachedSid)return Tt.__cachedSid; at the top of the function and changes its last return e} to return Tt.__cachedSid=e}, so whoami.exe runs once per extension host instead of four times a second per chat. The SID, the Windows account identifier that whoami.exe prints, does not change while the process runs, so nothing else changes.

Edit 3 (optional, server chatter). Same file. syncDebounceMs:5e3 becomes syncDebounceMs:60000 and the clamp syncDebounceMs:{min:250, becomes syncDebounceMs:{min:60000,, so each mounted store is listed once a minute instead of every five seconds. This does not affect tool latency; the CPU profile put 97% of the extension host’s time in the whoami spawn, so edit 2 is the one that matters. If your workflow depends on those stores updating quickly (cloud agents writing into a store from another machine, for example), skip edit 3 by deleting the line that mentions syncDebounceMs before running the script; the window would otherwise see those changes up to a minute later. I also turned off the on-demand full sync rounds here; that is the one I am not recommending for anyone who uses cloud agents, so it is not in the script.

Save this as cursor-agents-workaround.ps1 and run it with Cursor closed: powershell -ExecutionPolicy Bypass -File cursor-agents-workaround.ps1. It prints how many times each anchor matched (expect 1 and True) and writes nothing if an anchor is missing, so a future build that changes the code is left alone. Per-user installs live under %LOCALAPPDATA%\Programs\cursor, which is the default. A system-wide install is under C:\Program Files\cursor, needs an elevated PowerShell, and takes -app "C:\Program Files\cursor\resources\app".

param([string]$app = "$env:LOCALAPPDATA\Programs\cursor\resources\app")
$glass = "$app\out\vs\workbench\workbench.glass.main.js"
$exec  = "$app\extensions\cursor-agent-exec\dist\main.js"
$utf8  = [Text.UTF8Encoding]::new($false)

$s = [IO.File]::ReadAllText($glass)
$a = 'this._parkInlineDiffs=this.experimentService.checkFeatureGate("park_inline_diffs"'
$n = ([regex]::Matches($s, [regex]::Escape($a))).Count
"glass bundle: anchor found $n time(s), expect 1"
if ($n -eq 1) {
  Copy-Item $glass "$glass.orig" -Force
  [IO.File]::WriteAllText($glass, $s.Replace($a, 'this._parkInlineDiffs=!0||this.experimentService.checkFeatureGate("park_inline_diffs"'), $utf8)
  "glass bundle: patched, stock copy saved as $glass.orig"
} else {
  "glass bundle: left alone (already patched, or this build changed the code)"
}

$s = [IO.File]::ReadAllText($exec)
$m = [regex]::Match($s, 'function (\w+)\(\)\{const (\w+)=String\(\(0,\w+\.execFileSync\)\("whoami"')
"agent-exec bundle: whoami function found: $($m.Success)"
if ($m.Success) {
  $fn = $m.Groups[1].Value
  $v  = $m.Groups[2].Value
  Copy-Item $exec "$exec.orig" -Force
  $s = $s.Replace('function ' + $fn + '(){const ' + $v + '=String(', 'function ' + $fn + '(){if(void 0!==' + $fn + '.__cachedSid)return ' + $fn + '.__cachedSid;const ' + $v + '=String(')
  $s = $s.Replace('lock identity",lockPath:""});return ' + $v + '}', 'lock identity",lockPath:""});return ' + $fn + '.__cachedSid=' + $v + '}')
  $s = $s.Replace('syncDebounceMs:5e3', 'syncDebounceMs:60000').Replace('syncDebounceMs:{min:250,', 'syncDebounceMs:{min:60000,')
  "agent-exec bundle: __cachedSid now occurs $(([regex]::Matches($s, '__cachedSid')).Count) time(s), expect 3"
  [IO.File]::WriteAllText($exec, $s, $utf8)
  "agent-exec bundle: patched, stock copy saved as $exec.orig"
} else {
  "agent-exec bundle: left alone (already patched, or this build changed the code)"
}

I tested the script on copies of the stock 3.20.0 files. The patched Agents window bundle came out byte for byte identical to the one running here, the agent-exec bundle carries exactly the four edits above and passes node’s syntax check, and a second run leaves both files alone. Result on this machine since Sep 6, through 3.19.14, 3.19.16 and 3.20.0: zero “Potential working copy LEAK” lines in renderer.log, and the Agents window tool call median at 10 to 50 ms instead of 8.8 s, measured with edit 2, edit 3 and the on-demand change together.