Title: An open Canvas leaks PNG snapshots into blob_storage (46 GB in ~3 h); once the blob quota is full, Remote-SSH gets stuck in an endless reconnect loop
Summary
I left an agent-generated Canvas open in an editor tab. For as long as it stayed open, Cursor stored a new 1440×1000 PNG snapshot of it about once a second and never released any of them. ~/Library/Application Support/Cursor/blob_storage reached 46 GiB in about 2.5 hours. That matches Chromium’s blob disk quota (10% of my 460 GiB disk). After that my Remote-SSH window stopped working: it reconnected every 20 seconds, the Explorer stayed empty, editors were blank, and it logged “The remote extension host took longer than 60s to send its ready message”. Reloading the window didn’t help. Only quitting Cursor completely (Cmd+Q) fixed it.
Environment
- Cursor 3.15.6 (darwin-arm64) when this happened. The remote server was commit a1f686545fd0ce8917bbd2449f733551a9bce420.
- Cursor auto-updated to 3.22.7 (commit 37076c6c3f9e253c0fa2305197e45befd13a2260, VS Code 1.128.0) when I restarted.
- macOS 26.5.2 (25F84), Apple M5, 24 GB RAM, 460 GiB disk
- anysphere.remote-ssh 1.1.16, connected to a Linux x64 host
1. Canvas snapshot leak (main issue)
Steps
- Have the agent create a Canvas. Mine was a static ~160 KB
.canvas.tsxreport page with tabs. It contains no timers or animations, and notoBlob,toDataURLorcreateObjectURLcalls. - Leave the canvas tab open. I left it open for a few hours after the conversation ended.
- Watch
du -sh ~/Library/Application\ Support/Cursor/blob_storage.
Expected: blob_storage stays small.
Actual:
blob_storage/<session>/filled up with 9,610 page files (46 GiB). Nearly all were written between about 20:50 and just before midnight; the peak was 25.5 GB in a single hour.- 9,603 of those files held a PNG snapshot of the canvas (IHDR 1440×1000, 8-bit RGB, about 5.2 MB each).
- 9,369 files were exactly one PNG each.
- The other 234 had a small remote-protocol message blob in front of the PNG.
- Files written at 21:37, 22:05 and 22:42 are byte-identical (same MD5). The unchanged canvas was being captured over and over.
- The growth started when the canvas was created (20:51). It continued for about 2 hours after the agent conversation had finished (21:32), and stopped only when the quota was full.
- Closing the canvas tab released nothing (still 46 GiB and 9,610 files). Only a full quit freed it, because Chromium deletes the old blob directory at startup.
- I haven’t re-tested this on 3.22.7 yet.
2. Why this took down Remote-SSH (the code path looks fixed in 3.22.7)
In 3.15.6, BrowserWebSocket received each message as a Blob and read it with a single FileReader that had only an onload handler (de-minified from workbench.desktop.main.js):
this._fileReader.onload = e => { this._isReading = false; this._onData.fire(e.target.result); /* then read the next queued blob */ };
const enqueue = blob => { if (this._isReading) { this._queue.push(blob); return; } this._isReading = true; this._fileReader.readAsArrayBuffer(blob); };
Once the blob quota was exhausted (BlobStatus::ERR_OUT_OF_MEMORY), readAsArrayBuffer failed. _isReading then stayed true forever, so every later message was queued and never delivered. What followed:
- The client timed out every 20 seconds:
received socket timeout event (unacknowledgedMsgCount: 351, timeSinceOldestUnacknowledgedMsg: 20439, timeSinceLastReceivedSomeData: 20401). - The server logged
The client has reconnected.every 20 seconds. - On every reconnect the server replayed the same ~7 MB unacknowledged backlog. netstat showed 7,007,480–7,007,536 bytes delivered to the renderer’s WebSocket each time, and then the connection stalled again.
- The remote server was idle, its socket queues were empty, and SSH round trips were ~50 ms.
- Reloading the window doesn’t help because the blob quota is shared by the whole app.
In 3.22.7, BrowserWebSocket sets binaryType = "arraybuffer" and no longer uses a FileReader, so this particular path seems fixed. I’m including it because it’s what turned a disk leak into a full outage. Any other code that reads Blobs through a FileReader without onerror would fail the same way.
3. Minor: orphaned ssh processes
Each window reload during the loop left the previous ssh -T -D <port> ... bash --login -c bash process running with PPID 1. Eight of them piled up in about an hour (remote-ssh 1.1.16).
Suggested fixes
- Canvas: don’t re-capture a canvas that hasn’t changed; release or revoke snapshot blobs and object URLs; cap how many snapshots are kept.
- Wherever Blobs are read through a FileReader: handle
onerrorandonabort. - Remote-SSH: kill the ssh child process when its extension host exits.
Does this stop you from using Cursor?
Yes. The Remote-SSH window was unusable until I fully quit Cursor. I can share renderer.log, Remote - SSH.log and main.log on request.