Forked subagents are persisted hundreds of times each, growing state.vscdb to 30 GB in one day

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

A single Task(…) subagent launch is written to state.vscdb thousands of times. Each duplicate
carries a full copy of the parent conversation’s bubbles. On a one-day-old user-data-dir with
3 chats and 58 real subagent launches, this produced 25,331 composer headers, 2.5 million
cursorDiskKV rows, and a 30 GB state.vscdb.

The duplicated rows are loaded into the renderer at startup, which pins the UI thread at 100% and
makes the editor unusable. Deleting the entire user-data-dir does not help — the leak restarts on the
first forked subagent in a new chat.

Steps to Reproduce

Delete the user-data-dir entirely. Start Cursor. Zero chats present.

Open a new agent chat.

Launch a subagent in fork mode: Task(subagent_type=“recon-code”, resume=“self”, prompt=…).

Inspect User/globalStorage/state.vscdb.

Expected Behavior

no sluggish UI, no crashing cursor several times a day would be nice

Operating System

MacOS

Version Information

Version: 3.15.19
VS Code Extension API: 1.128.0
Commit: de07bee81cefe43461ebf4f40c3d2d78d15052a0
Date: 2026-08-11T05:22:54.627Z
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.3.0

For AI issues: which model did you use?

NA

Additional Information

Observed

That one Task call produced 8,428 rows in composerHeaders, all sharing:

the same subagentInfo.toolCallId
(call-d8afe4b7-e3f1-4179-9a32-aa136af37908-11 / fc_1fa2e765-382a-9b6a-9238-95a93c9bea10_1)

the same name (“Restore GEM-75 leftover state”)

the same subagentInfo.forkedFromComposerId (the new parent chat)

identical createdAt and lastUpdatedAt, to the millisecond

an identical composerData payload of exactly 1,596,689 bytes / 222 bubbles

Two composerId shapes appear for the same launch: task- and a bare .

– one real tool call, thousands of stored composers
SELECT json_extract(value,‘$.name’) AS name,
COUNT(*) AS stored,
COUNT(DISTINCT json_extract(value,‘$.subagentInfo.toolCallId’)) AS real_launches
FROM composerHeaders WHERE isSubagent=1
GROUP BY 1 ORDER BY stored DESC;

name stored real_launches
Restore GEM-75 leftover state 8428 1
Recon letter-body live resolve 4162 1
Implement live letter resolve 2051 1
Find GoldCard DR UI surface 1647 1

Totals after ~21 hours:

Real top-level chats

3 (17 composers incl. drafts/archived)

Real subagent launches (distinct toolCallId)

58

Subagent rows in composerHeaders

23,738 → 25,331 (~409 copies per launch)

cursorDiskKV rows

2,503,437 clone rows + 7,845 real

bubbleId rows

1.94 million / 21.8 GB

state.vscdb

30 GB

Only 60 subagent composers are actually referenced by any parent’s subagentComposerIds /
subComposerIds. The other ~25,000 are unreachable duplicates.

Consequences

Startup loads every duplicate. main.log, one second after launch:

[LocalAgentStorage] Scanned 2 recent databases (skipped 0 old), found 21952 agent headers, deduped to 21952

The dedup step does not collapse them, despite identical toolCallId and timestamps.

The renderer never goes idle. A 5-second sample of Cursor Helper (Renderer): 3,372 of 3,372
samples on com.apple.main-thread, dominated by V8 work under
node::PrincipalRealm::messaging_deserialize_create_object. Sustained 137–310% CPU, 2.3 GB footprint.
Developer-tools GC has no effect — this is live work on 25,000 phantom agents, not collectable garbage.

Writes cannot keep up. state.vscdb-wal sits pinned at 2.0 GB. The file grew 24 GB → 30 GB
during a single 40-minute conversation. The previous session’s main.log contains tens of thousands of:

[error] [uncaught exception in main]: Error: write EIO A system error occurred (write EIO)

Wiping the user-data-dir is not a workaround. It was deleted entirely; the 30 GB returned within
one day, starting from the first forked subagent in the first new chat.

Contributing factor

Every duplicate has subagentInfo.forkedFromComposerId set, i.e. all were launched with
resume: “self”. Fork mode is what makes each duplicate 1.6 MB (a full parent-conversation copy)
rather than a ~2 KB header. That turns the duplication bug from an annoyance into a 30 GB file. The
duplication itself looks independent of fork mode; fork mode only sets the per-copy cost.

Expected

One Task(…) invocation should persist one subagent composer. Repeated writes for the same
toolCallId should upsert, and forked subagents should reference the parent conversation rather than
copying its bubbles per duplicate.

Workaround in use

Rebuild state.vscdb, keeping only composers that are top-level or referenced by a parent’s
subagentComposerIds / subComposerIds. 77 composers kept, ~2.5 million rows dropped,
30 GB → 1.3 GB. Must be done with the app quit.

Does this stop you from using Cursor

Yes - Cursor is unusable

Thanks for the report @Keith_Minkler. This looks like the same issue that was reported here.

You can checkout Mohit’s advice in the other thread, and I’ve added your report to our issue tracker!