Bug: Claude Code Cursor extension host leaks to 37GB RAM, causes OOM cascade (v2.1.94)
Environment
- Extension:
anthropic.claude-code-2.1.94-linux-x64(Cursor marketplace) - Cursor:
cursor-servercommita80ff7dfcaa45d7750f6e30be457261379c29b00(remote SSH) - Host: Ubuntu 24.04, kernel 6.8.0-107, 48GB RAM, 8GB swap
- Node: Extension host process (
bootstrap-fork --type=extensionHost)
Bug
The Claude Code Cursor extension's extension host process leaked to ~37 GB RSS (78% of system memory) over 2h47m, triggering a system-wide OOM cascade. The kernel OOM killer took out two Chrome processes, and Postgres was next in line (it was invoking the OOM killer itself). Load average hit 290+. This happened twice in one day on the same machine.
The runaway extension host started at 20:38:00 UTC. At 20:38:16 UTC the user launched claude --dangerously-skip-permissions from the integrated terminal. 4 other Cursor windows with Claude Code were open simultaneously — only this one leaked. The other 4 stayed at 0.4–0.6% MEM.
Extension log evidence
From ~/.cursor-server/data/logs/20260407T203707/exthost1/Anthropic.claude-code/Claude VSCode.log:
20:38:06.612 [info] Claude code extension is now active?
20:38:06.612 [info] MCP Server running on port 37577 (localhost only)
20:38:20.344 [info] New WS connection from: /
20:38:20.344 [info] [DiagnosticStreamManager] Started streaming diagnostics
21:29:35.593 [info] diff from .claude/rules/ponder-capabilities.md to ...
21:45:22.134 [info] Closing all diff tabs in the editor... Closed 0 diff tabs.
21:45:23.347 [info] diff result undefined ✻ [Claude Code] ponder-capabilities.md ...
22:17:38.019 [info] [DiagnosticStreamManager] Notifying 1 clients about diagnostics change for 2 files
22:32:36.890 [info] Closing all diff tabs in the editor... Closed 0 diff tabs.
[ ... LOG GOES SILENT — 52 minutes until process killed at 23:24:54 ... ]
Key patterns
Closing all diff tabs... Closed 0 diff tabsappears repeatedly. The cleanup runs but never finds tabs to close — suggesting stale references accumulating in memory that the cleanup can't reach.
diff result undefinedat 21:45:23 on the same file that was diffed earlier. If this code path doesn't release allocated resources when the diff fails/returns undefined, it leaks heap.
- Log silence for 52 minutes while the process was still running and consuming memory. The most likely explanation: V8 heap exhausted, even the logger can't allocate. The process didn't die because swap kept it alive.
- MCP Server on localhost:37577 — WebSocket connections that retain per-message state without bounded backpressure are a classic leak vector.
Impact
| Metric | Value |
|--------|-------|
| Process RSS at kill | ~37 GB (78.2% of 48GB) |
| Process CPU at kill | 75% |
| Process elapsed | 2h 46m 54s |
| Load average | 290.59 / 242.90 / 118.18 |
| OOM kills before intervention | 2 Chrome processes |
| SIGTERM response | Ignored (had to SIGKILL) |
| Memory recovered after kill | 36 GB immediately |
Reproducer hints
- Open Cursor IDE remote-SSH'd to a Linux host
- Open a project with multiple
.claude/rules/*.mdfiles - Run
claude --dangerously-skip-permissionsin the integrated terminal - Have the agent open/close diff views on rules files repeatedly via MCP
- Monitor:
ps -o pid,pmem,rss,etime -p <extensionHost-pid> - Wait ~2-3 hours under active use
Not a guaranteed repro — only 1 of 5 concurrent extension hosts leaked. It's a rare runaway condition, not a baseline rate.
Suggested fixes
--max-old-space-size=4096on the extension host node spawn — 4GB heap is plenty; crash cleanly rather than dragging the host down- Diff-tab lifecycle audit — the "Closed 0 diff tabs" pattern suggests stale references. The
undefineddiff result path needs to release resources whether the diff succeeded or not - MCP WebSocket backpressure — per-connection caps on queued messages, drop metrics
- Memory watchdog — if extension RSS crosses 1GB, write a heap snapshot; at 2GB, warn; at 4GB, self-restart
- Liveness signal — if the extension log stops emitting for >5 minutes while the process is running, that's a strong "stuck/leaked" signal
Workarounds we applied (server-side)
NODE_OPTIONS=--max-old-space-size=8192globally (caps all V8 heaps at 8GB)- systemd OOM watchdog timer that kills node processes at 8GB RSS
- Docker/Postgres OOM score protection so databases survive before dev tools
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗