Codebase snapshot staging packs leak to disk on canceled push; extra roots duplicated per window (10+ GiB leftover)

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

Each workspace window registers 5 codebases under workspaceStorage / anysphere.cursor-retrieval / codebaseTelemetry:codebases:{uuid}:

kind path
0 project folder
6 ~/.cursor
7 ~/.claude
8 ~/.codex
9 ~/.agents

Kinds 6–9 are the same absolute paths in every window, but each window assigns its own UUID. With 7 windows open, ~/.codex has 7 distinct codebase UUIDs, each generating its own git pack.

On extension-host restart (renderer closed MessagePort), anysphere.cursor-retrieval fires onCommand:cursor.codebaseTelemetry.triggerSnapshot. Within 0.5–4 seconds the push is canceled:

[ExtHostCursor] Failed to push plugins snapshot Canceled

This is not a timeout or network error. Concurrent eager activation of anysphere.cursor-agent-exec (activationEvent: *) races with the snapshot. The MCP cursor_mcp_lease_snapshot_store write starts after the cancel has already fired.

The canceled push leaves fully-written staging packs on disk:

~/Library/Application Support/Cursor/snapshots/codebases/<uuid>/staging/*.pack

These are never cleaned up. Each subsequent window open, agent resume, extension-host restart, or MCP lease rebuild writes another pack.

Evidence (2026-08-19)

Trigger frequency. 6 triggerSnapshot firings that evening: 13:40, 15:35, 17:14, 18:30, 21:08, 23:24. 18 Failed to push plugins snapshot Canceled lines across workbenches.

Exact log pairs (window2_wb1 exthost.log, session 20260819T141944):

2026-08-19 17:14:21.540 triggerSnapshot (root cause: anysphere.cursor-retrieval)
2026-08-19 17:14:23.613 Failed to push plugins snapshot Canceled

2026-08-19 18:30:28 → 18:30:29  (same pattern)
2026-08-19 21:08:23 → 21:08:26
2026-08-19 23:24:15 → 23:24:20

Leftover .codex packs. Three staging packs from three different workspace windows (projects, majia-huiyuan, Popskill-release), each 3,457,400,144 bytes (3.22 GiB). Different SHA256 hashes – full re-packs of the same tree, not copies. Source directory: ~/.codex/archived_sessions/ (4.6 GiB, 569 jsonl files).

Leftover .claude packs. Four staging packs, 341,115,543–341,115,835 bytes each (~325.31 MiB). Source: ~/.claude (projects, skills, plugins). After manual cleanup, a new session at 00:02 wrote a pack of 341,122,546 bytes (+7 KB vs prior), confirming .cursorignore placed under ~/.claude did not reduce pack size. The loop continued writing new 325 MiB packs at 00:15 and 00:21 on 2026-08-20.

Disk impact.

  • Peak leftover staging: 10.93 GiB
  • Total snapshots directory peak: ~15 GiB (codebases 11 GiB + plugin stores 4.0 GiB)
  • After manual deletion of leftover staging: 4.7 GiB, then grew again as new packs appeared

Actual behavior

Unbounded disk growth. The same large directory trees are packed once per window per extension-host restart. Canceled pushes leave multi-GiB staging files. No settings toggle exists. .cursorignore has no effect on pack size.

Steps to Reproduce

  1. Open 3+ workspace windows on different projects.
  2. Wait for or trigger an extension-host restart (closing and reopening a window is sufficient).
  3. Observe triggerSnapshot followed by Failed to push plugins snapshot Canceled in the extension host log.
  4. Check ~/Library/Application Support/Cursor/snapshots/codebases/ for leftover .pack files under staging/.
  5. Repeat step 2. Each cycle adds another full pack per extra root per window.

Expected Behavior

  1. Shared extra roots (~/.codex, ~/.claude, ~/.cursor, ~/.agents) should use one codebase ID per absolute path, not one per workspace window.
  2. A canceled or aborted snapshot push must clean up its staging packs before returning.
  3. Agent session archives and plugin caches should not be packed as project codebases. .cursorignore and .cursorindexingignore on those roots should be honored.
  4. There should be a user-visible setting to disable codebase and plugin snapshots.

Operating System

MacOS

Version Information

Cursor IDE
Version: 3.16.29
VSCode Version: 1.128.0
Commit: 6246455961129c32969845e00aa25d87ae926ec0
Date: 2026-08-18T01:26:26.285Z
Build Type: Stable
Release Track: Default
OS: Darwin arm64 27.0.0

7 workspace windows open concurrently.
Only non-default user setting: window.autoDetectColorScheme: true

Additional Information

Requested fixes

These are bugs, not optimization requests.

  1. Clean up staging on cancel/abort. When the snapshot push is canceled, delete or roll back the staging pack. Do not leave multi-GiB files on disk.
  2. Deduplicate extra codebases by absolute path across windows. ~/.codex is one directory. It should have one codebase UUID regardless of how many workspace windows are open.
  3. Honor ignore files on extra roots. Do not snapshot ~/.codex/archived_sessions/, ~/.codex/sqlite/, ~/.claude/projects/, ~/.claude/plugins/cache/, or similar paths. Respect .cursorignore and .cursorindexingignore placed at those roots.

Local workaround

I placed .cursorignore and .cursorindexingignore at ~/.codex (excluding archived_sessions/, sqlite/, cache/) and ~/.claude (excluding projects/, plugins/cache/). I manually delete leftover staging packs periodically. This does not fix the cancel race condition or the per-window UUID duplication.

Does this stop you from using Cursor

Sometimes - I can sometimes use Cursor

Follow-up with root-cause details after reading the bundled JS/native strings in 3.16.29, plus fresh evidence from today. Hopefully this saves some localization time.

Correction to my original post

I attributed the leak to the log line [ExtHostCursor] Failed to push plugins snapshot Canceled. That line belongs to a separate pipeline: ExtHostCursor pushing a plugins-state snapshot to the main thread via $updatePluginsSnapshot, and an extension-host restart cancels that RPC. It is correlated in time with the pack leak (same EH restart) but is not its cause. The codebase pack upload logs under [SnapshotUploader] / [PackfileService] instead.

Code-level findings

1. The leak mechanism is cleanup-skip on dispose, plus no startup sweep. Packs are generated by the native snapshot client (gix-snapshot / codebase-snapshot-history strings) into staging/. The JS uploader calls cleanupPackfile in a finally block after the upload attempt — but when the snapshot manager has been disposed (extension host restarting), the path is:

cleanupPackfile: manager disposed, skipping

There is no startup sweep for orphaned staging/pack-* files. So every “generate pack → EH restarts mid-upload → dispose” cycle orphans one pack. Consistent with this: today at 12:14 one codebase’s staging was correctly emptied after an upload attempt while the manager was alive — cleanup works on the normal path; orphans happen only on dispose.

2. A 3.2 GiB pack can effectively never finish uploading. Upload is sequential chunked: server-provided chunk size capped at 67,108,864 bytes (64 MiB), per-chunk RPC timeout 600,000 ms, create/complete timeouts 30 s / 60 s. There is no resume: a failed attempt gets a fresh uploadUuid and restarts from chunk 0, and the pack is regenerated each cycle (same size, different SHA256 — matching my original report). A 3.2 GiB pack is ~53 sequential chunks; my extension hosts restarted 62 times between 2026-08-20 00:00 and ~12:40 (Extension host terminating: renderer closed the MessagePort), so no attempt survives long enough.

3. Per-window UUIDs are by design of the dedup scope. getOrCreateCodebase dedups by (authId, path, kind) within each window’s own workspaceState memento, otherwise assigns crypto.randomUUID(). There is no cross-window/global dedup by absolute path. The extra roots are hardcoded (kinds: 6 = ~/.cursor, 7 = ~/.claude, 8 = ~/.codex, 9 = ~/.agents; the latter three behind a requiresGate flag). 7 windows → 7 UUIDs per shared root → 7 identical multi-GiB packs.

4. Why .cursorignore has no effect here. Pack contents come from the shadow git object database built by the native snapshotter, not from a file walk at pack time. A .cursorignore placed at the root of ~/.codex is simply committed as another blob (my pack grew by exactly the size of the added files). .cursorindexingignore applies to the indexing/merkle pipeline, not to this snapshot pipeline. The per-dot-dir allowlists/denylists in the bundle only gate file-watch events, not what gets packed.

5. Gating, and the only user-side switch that appears to work. The pipeline runs only when the account privacy mode allows usage data + codebase training (internal check: mode === 4) and the feature gates codebase_telemetry_v2 and — for the home dot-dirs — codebase_telemetry_v2_agent_dot_dirs are enabled. Upload destination is aiserver.v1.CodebaseSnapshotService on the configured backend URL. There is no dedicated snapshot toggle; switching the privacy mode away from “usage + training allowed” appears to be the only user-side way to stop the pipeline. Would be good if staff can confirm.

Fresh evidence (2026-08-20)

  • After deleting 10.93 GiB of orphaned staging at ~00:05, the loop recurred: at 12:32:43 a new staging pack of 3,510,122,834 bytes appeared under the same ~/.codex codebase UUID (64835b94-…), ~52.7 MiB larger than yesterday’s because the source directory grew. It was held open by a Cursor process at the time of writing.
  • 19 more Failed to push plugins snapshot lines between 00:20 and 10:46 (17 Canceled, 2 network aborts); zero log evidence of any successful pack upload; 62 extension-host restarts.
  • snapshots/ total back up to 9.3 GiB; the separate snapshots/stores/ grew from 4.0 to 6.0 GiB.

Sharpened fix requests (supersedes the three in my original post)

  1. Run cleanupPackfile (or equivalent) on manager dispose, and add a startup sweep that deletes orphaned staging/pack-* files.
  2. Make uploads resumable (persist uploadUuid + chunk offset), or at least reuse the already-generated pack instead of regenerating multi-GiB packs each cycle.
  3. Deduplicate extra-root codebases by absolute path across windows — one UUID per path per account, not per window.
  4. Honor .cursorignore / .cursorindexingignore (or a documented exclusion list) in the snapshot pipeline; do not pack agent session archives like ~/.codex/archived_sessions/ (4.6 GiB) by default.
  5. Expose a user-visible setting for codebase/plugin snapshots instead of tying it only to the privacy mode.

Happy to provide full logs or run a debug build if that helps.