Windsurf Language Server Kernel Panic

Orphaned language server processes spiraling out of control, each holding 1.3 GB of memory hostage.
Executive Summary
Windsurf 2.3.15 introduced a process lifecycle regression in which the language server health check spawns a new instance every 60 seconds when the existing server is unresponsive, but never terminates the previous instance. On an 8 GB machine, this creates a guaranteed kernel panic in approximately 20 minutes.
The pre-update version had an excessive disk write bug that caused macOS to forcefully terminate language server processes. While itself a defect, this acted as an unintentional safety valve—dead processes freed memory. The 2.3.15 fix removed the disk write issue but also removed the only mechanism preventing unbounded memory growth.
The Timeline: From Stable to Catastrophic

Pre-2.3.15 (left): processes killed cleanly by macOS. Post-2.3.15 (right): 21 orphans accumulate unchecked.
May 3, 2026: Baseline — Clean System
0 language server issues, 0 Jetsam events, 0 crash reports. System healthy.
May 12–13, 2026: Phase 1 — Disk Write Bug Active
6 crash reports from macOS disk write enforcement. Servers writing 2–34 GB. Killed and replaced cleanly. Memory freed. System stable.
The Unintentional Safety Valve
PIDs changed across events (2980 → 15577, 2815 → 13155). Old processes terminated. New ones spawned. Memory was freed. Maximum concurrent instances: 1–2. System survived.
May 16, 2026: Stable Window
No issues. 0 crash reports, 0 Jetsam. Normal operation.
May 27, 2026 (02:21 AM): Windsurf 2.3.15 Installed
Updated binary. Disk write fix deployed. Retry-without-kill behavior introduced.
May 28, 2026 (12:41 PM): First Jetsam Event Post-Update
macOS killed WebKit for memory. First sign of background memory pressure. Language server logs show normal start/shutdown (2 starts, 2 shutdowns).
May 29, 2026 (14:34–14:55): Catastrophic Spawn Loop → Kernel Panic
20 language servers spawned in 19 minutes. 28.7 GB consumed. 14 MB free. Kernel forced hardware reset. Pink screen of death.
The Spawn Loop: 60 Seconds to Doom

The spawn loop: one new language server every 60 seconds, memory climbing from 1.3 GB to 28.7 GB in 20 minutes.
The Fatal Sequence
14:34:10 — Starting language server process with pid 2972 [ 1.3 GB total ]
14:35:08 — Starting language server process with pid 3013 [ 2.6 GB total ]
14:36:08 — Starting language server process with pid 3041 [ 3.9 GB total ]
14:37:08 — Starting language server process with pid 3064 [ 5.2 GB total ]
14:38:08 — Starting language server process with pid 3085 [ 6.5 GB total ]
14:39:08 — Starting language server process with pid 3105 [ 7.8 GB total ] ← Exceeds physical RAM
14:40:08 — Starting language server process with pid 3129 [ 9.1 GB total ]
14:41:09 — Starting language server process with pid 3153 [ 10.4 GB total ]
14:42:09 — Starting language server process with pid 3177 [ 11.7 GB total ]
14:43:09 — Starting language server process with pid 3206 [ 13.0 GB total ]
14:44:09 — Starting language server process with pid 3227 [ 14.3 GB total ]
14:45:09 — Starting language server process with pid 3249 [ 15.6 GB total ]
14:46:09 — Starting language server process with pid 3271 [ 16.9 GB total ]
14:47:09 — Starting language server process with pid 3292 [ 18.2 GB total ]
14:48:09 — Starting language server process with pid 3312 [ 19.5 GB total ]
14:49:09 — Starting language server process with pid 3332 [ 20.8 GB total ]
14:50:09 — Starting language server process with pid 3353 [ 22.1 GB total ]
14:51:10 — Starting language server process with pid 3377 [ 23.4 GB total ]
14:52:10 — Starting language server process with pid 3404 [ 24.7 GB total ]
14:53:10 — Starting language server process with pid 3838 [ 26.0 GB total ]
14:55:16 — ████████ KERNEL FORCED HARDWARE RESET ████████ [ 28.7 GB final ]

Memory usage over 24 hours: slow overnight creep, then vertical spike at 14:34 as the spawn loop begins.
Root Cause: The Retry-Without-Kill Pattern
The bug is simple and devastating:
The Fatal Feedback Loop
- Memory Pressure Builds — Existing LS + system processes fill RAM
- LS Becomes Unresponsive — gRPC calls timeout under swap pressure
- Health Check Timeout — Extension detects "dead" server
- SPAWN NEW INSTANCE — Old instance NOT killed (+1.3 GB) ← THE BUG
- +1.3 GB Memory Used — Orphan holds allocation forever
- Loop back to step 1 — Every 60 seconds
After ~20 iterations (20 minutes) → KERNEL PANIC
Why the Old Instances Never Die
When the extension spawns a new language server, the old process becomes orphaned:
- Parent: unknown (adopted by launchd PID 1)
- Flags: isImpDonor, isLiveImpDonor
- Memory: 1.3 GB RSS (never freed)
- Lifetime: Indefinite (no kill signal sent)
The extension has no reference to the old PID. It spawns a new one and moves on. The orphan continues to exist, holding 1.3 GB hostage, until the kernel runs out of options.
Pre vs Post: A Tale of Two Bugs

Five sysdiagnose snapshots across May 2026 — the Windsurf 2.3.15 update (May 27) marks the inflection point.
| Metric | Pre-2.3.15 (Jan–May 26) | Post-2.3.15 (May 27+) |
|---|---|---|
| Max concurrent instances | 1–2 | 21+ (unbounded) |
| Instance lifetime | Minutes (killed by macOS) | Indefinite (orphaned) |
| Disk writes per instance | 2–34 GB (excessive) | Normal |
| Total memory ceiling | ~2.6 GB (natural limit) | None (28.7 GB observed) |
| Kernel panics | 0 | 1 (confirmed) |
| Self-healing mechanism | Yes (crash = cleanup) | None |
The Fix: Kill Before Spawn
The solution is straightforward:
Correct Process Lifecycle
- Health Check Fails — Server unresponsive
- SIGTERM Old PID — Wait 5s → SIGKILL ★ THE FIX
- Memory Freed — -1.3 GB reclaimed
- Spawn New LS — Max retries: 3
- Stable — 1 instance, net memory change: 0 GB
Implementation Checklist
| # | Fix | Effort | Impact |
|---|---|---|---|
| 1 | Kill before spawn — SIGTERM → wait 5s → SIGKILL old PID before starting new | 1 line | Prevents all accumulation |
| 2 | Max retry count — Stop after 3 consecutive failures | 3 lines | Prevents infinite loop |
| 3 | Memory budget — Refuse to spawn if total LS RSS > 4 GB | ~10 lines | Hard ceiling |
| 4 | Orphan self-destruct — LS checks if parent PID changed to 1, exits | ~5 lines (Go) | Cleanup on detach |
| 5 | User notification — Show error after max retries instead of silent spawn | ~15 lines | UX improvement |
Collateral Damage
The spawn loop didn't just crash the system—it took down critical infrastructure along the way:
| Component | Impact | Duration |
|---|---|---|
| mds (Spotlight) | 2.1 GB disk writes — swap thrashing | 11:40–14:50 (3.2 hours) |
| BCMWLAN WiFi Chip | Chip Trap — driver crashed from resource starvation | 00:49, 14:33 |
| DNS Resolution | DNSFailureRecovery reassociation attempts | 00:49, 14:33 |
| WiFi Radio | Power OFF — disabled by crash | At sysdiagnose |
| Network | All connectivity lost — "Not Reachable" | At sysdiagnose |
| AWDL/AirDrop | Disabled | At sysdiagnose |
By the time the kernel forced a reset, the WiFi chip had crashed, network connectivity was lost, and Spotlight was thrashing the disk trying to keep up with memory pressure. This wasn't just a language server bug—it was a cascading system failure.
Forensic Artifacts
| Artifact | Value |
|---|---|
| Crash Incident ID | 7A5B4799-2C8A-4624-862E-80AD1DA9BD08 |
| Reset Counter ID | 07C15587-6C87-4200-A92E-DBDD605AF9B5 |
| Bug Type | 151 (forceReset) |
| Crash Session Log | 20260529T143355 |
| Windsurf Version | 2.3.15 |
| Binary Date | 2026-05-27 02:21 |
| Extension Version | 0.2.0 |
| Codeium Team ID | 83Z2LHX6XW |
| macOS Build | 25E253 (macOS 26.4.1) |
| Hardware | MacBookPro17,1 (M1, 8 GB) |
Lessons Learned
1. Sometimes the Bug Is the Safety Valve
The disk write bug was annoying, but it prevented unbounded memory growth. Removing it without adding proper process lifecycle management created a worse problem.
2. Orphaned Processes Are Silent Killers
When a parent process loses track of its children, those orphans continue to consume resources indefinitely. Always track PIDs and kill old processes before spawning new ones.
3. Memory Overcommit Is Not Infinite
macOS can compress and swap, but 3.5× physical RAM is the breaking point. After that, the kernel has no choice but to force a reset.
4. Health Checks Need Circuit Breakers
A health check that retries forever without a max count or memory budget is a ticking time bomb. Always add defensive limits.