Xtension-host processes orphaned after window close/reuse — SIGTERM ignored, requires SIGKILL

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

Extension-host processes orphaned after window close/reuse — SIGTERM ignored, requires SIGKILL (root cause analysis + workaround)

Environment

  • Cursor 2.6.21
  • macOS 15 (Darwin 24.6.0)
  • Apple Silicon (M-series), 36 GB RAM

Bug Description

When a Cursor window is closed, and a different project reuses its slot, the old extension-host processes are never terminated. They enter a busy-wait loop at ~94% CPU per process and ignore SIGTERM — only SIGKILL works. Over hours/days, this accumulates and exhausts all system memory.

Root Cause Identified

Cursor assigns internal window slots (visible as [N-M] in process names via ps aux). When slot N is reused for a new project, new extension hosts [N-new] are spawned, but the old [N-old] hosts are never killed. The orphaned hosts enter a spin loop — likely waiting for IPC from a renderer that no longer exists.

Evidence

Machine with 3 Cursor windows open. ps aux shows 16 extension-host processes — 9 legitimate, 7 orphaned zombies:

Process CPU Accumulated Status
extension-host (user) Project-A [2-7] 93% 789 min (13 hours!) window closed hours ago — process leaked
extension-host (user) Project-B [3-13] 94% 188 min Window closed — leaked
extension-host (user) empty [4-16] 94% 56 min Slot 4 was reused by another project — old “empty” host orphaned
extension-host (user) empty [5-22] 93% 40 min Slot 5 reused — orphaned
extension-host (user) Project-C [6-31] 91% 19 min Window closed — leaked
extension-host (user) Project-C [7-37] 91% 17 min Window closed — leaked
extension-host (user) empty [6-28] 93% 28 min Slot 6 reused — orphaned
Total 648% 7 zombies, each pinning a CPU core

The [N-M] slot numbers prove the pattern: slots 4, 5, and 6 each have two extension hosts — the old orphan and the new legitimate one.

System Impact

  • 115 GB of memory committed on a 36 GB machine
  • macOS compressor holding 71 GB compressed into 18 GB RAM (4:1 ratio)
  • 31.5 GB swap used (out of 32 GB)
  • Load average: 78 (machine has ~12 cores)
  • System is nearly unusable

Key Finding: SIGTERM Is Ignored

kill 69042    # SIGTERM — process continues at 93% CPU
kill 69042    # again — no effect
kill -9 69042 # SIGKILL — process dies immediately

This confirms the extension host’s event loop or signal handler is stuck. Normal kill (SIGTERM) has zero effect.

After SIGKILL Cleanup

Metric Before After (45 seconds)
Load average 78 24 (dropping)
Free RAM 0.07 GB 5.4 GB
Swap 31.5 GB 11.0 GB
Compressor 71 GB equivalent 34 GB

Workaround

launchd watchdog that kills zombie extension-host processes every 5 minutes:

#!/bin/bash
# ~/bin/cursor-watchdog.sh
# Kills extension-host processes above 80% CPU (normal hosts never exceed 5%)
ps aux | grep "[C]ursor Helper (Plugin)" | grep "extension-host" | \
  awk '$3 > 80 {print $2}' | xargs kill -9 2>/dev/null
<!-- ~/Library/LaunchAgents/com.user.cursor-watchdog.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
  "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.user.cursor-watchdog</string>
    <key>ProgramArguments</key>
    <array>
        <string>/bin/bash</string>
        <string>/Users/YOU/bin/cursor-watchdog.sh</string>
    </array>
    <key>StartInterval</key>
    <integer>300</integer>
    <key>RunAtLoad</key>
    <true/>
</dict>
</plist>

Install: chmod +x ~/bin/cursor-watchdog.sh && launchctl load ~/Library/LaunchAgents/com.user.cursor-watchdog.plist

Suggested Fix

When a Cursor window is disposed, the main process should:

  1. Send SIGTERM to associated extension-host processes
  2. Wait a grace period (e.g., 5 seconds)
  3. Send SIGKILL to any that haven’t exited
  4. Or better: use a process group (setpgid) so closing the window kills the entire process tree

Related Issues

Steps to Reproduce

  1. Open 3+ different projects in Cursor as separate windows
  2. Close 2 of the windows (Cmd+W or File → Close Window)
  3. Open new projects in Cursor (reuses the closed window slots)
  4. Wait 5-10 minutes
  5. Run ps aux | grep "extension-host" | awk '$3>50' in Terminal
  6. Observe: old extension-host processes from closed windows are still running at ~94% CPU each
  7. Run kill <pid> — process survives. Run kill -9 <pid> — process dies.

Expected Behavior

Extension-host processes should terminate when their window is closed. At minimum, if SIGTERM fails, Cursor should escalate to SIGKILL after a grace period.

Operating System

MacOS

Version Information

Cursor 2.6.21

Does this stop you from using Cursor

Yes - Cursor is unusable

Hey, thanks for the incredibly detailed report. Root cause analysis, evidence, workaround, even links to the upstream VS Code issue. This is honestly one of the best bug reports I’ve seen.

We’re aware of the issue with orphaned extension-host processes when closing windows on macOS, and we’re seeing similar reports. I shared your analysis with the team. The part about SIGTERM being fully ignored and needing a SIGKILL fallback is especially useful. No timeline yet, but your report helps a lot with prioritization.

A few questions to help complete the picture:

  1. What extensions are installed? In Terminal run: cursor --list-extensions. In a similar case Severe Memory Leak (~4.16GB per window) & Massive Swap (28GB) AFTER Closing Project Window the root cause was third-party AI extensions like Claude Code, Codium, etc. After removing them, processes started shutting down correctly when closing the window. It’s possible extensions are making it worse in your case too.

  2. Does it reproduce with cursor --disable-extensions? If the processes are still orphaned with extensions disabled, it points to Cursor itself.

  3. Does Cmd+Q fully quit and kill all processes correctly, or do zombies remain even after a full quit?

Your launchd watchdog is a great workaround for now. If you can confirm it reproduces without extensions, that’s an even more valuable data point.

Let me know the answers to the points above.

  1. Let me double-check the provided extensions at the link. If I find anything else, I will provide more information.
  2. The cause may be both. Disabling extensions can help solve the problem, but it is not guaranteed.
  3. No zombie remains after a full quit.

Thanks for the replies.

Good to know Cmd+Q cleans up correctly. That confirms the issue is in the window close path, not the full quit flow. That’s a useful data point.

The extensions list is still the most important piece here. In the thread I linked, the user had Claude Code and Codium running alongside Cursor’s built-in AI, and after removing them, the orphaned processes stopped entirely, even when closing windows with the red X button. So extensions can absolutely be the difference between always orphans and never orphans.

When you get a chance, please run:

  1. cursor --list-extensions and paste the output here
  2. Try working for a session with cursor --disable-extensions, close a few windows, then check ps aux | grep extension-host | awk '$3>50' after 10 to 15 minutes

If zombies still appear with extensions disabled, that’s a clear Cursor-side bug. If they don’t, we can narrow down which extension is causing the spin loop.

In the meantime, using Cmd+Q between project switches is a reliable workaround since you confirmed it kills everything cleanly.