Windows: Grep tool abandons rg.exe children with unread stdout pipes - live-blocked process flood causes kernel timer storm and BSOD 0x133 (DPC_WATCHDOG_VIOLATION)
Summary
Claude Code on Windows (v2.1.207, VS Code extension host) can spawn bursts of hundreds of rg.exe (Grep tool) within ~2 minutes and then stops reading their stdout pipes. The children are not orphans and not exit-hangers — they are live processes permanently blocked in NtWriteFile on a full libuv named pipe. Each blocked rg/conhost pair churns high-resolution timers every ~15 ms; a few hundred pairs accumulate ~18,000 KTIMER2 objects, whose mass expiry keeps a CPU on DISPATCH_LEVEL for >12 s and bugchecks the machine with 0x133 DPC_WATCHDOG_VIOLATION (Arg1=1).
11 BSODs on one machine between 2026-07-07 and 2026-07-11. Full kernel dumps of 4 crashes were analyzed; evidence below.
Environment
- Claude Code 2.1.207 (CLI and VS Code extension), Windows 11 Pro 26100
- Lenovo ThinkPad T15g Gen 2i, 64 GB RAM, VBS/HVCI enabled
- Reproduces WITHOUT autonomous loops — plain interactive multi-session use is enough
Evidence from kernel dumps and live monitoring
- A process monitor (2-min sampling) captured the explosion live before crash #11 (2026-07-11 13:18):
- 13:12 healthy: 359 visible processes, 0x rg.exe
- 13:14: 966 visible processes, 291x rg.exe + 315x conhost.exe, threads 5,856 -> 10,178
- 13:18 BSOD 0x133. From healthy to dead in ~5 minutes.
- Kernel dump of crash #11: 696x rg.exe + 716x conhost.exe in the kernel process list; parent of all of them: one Code.exe (extension host) holding 5,827 handles. Crash #9 dump: 4,140 processes total, ~1,250 rg.exe + ~1,262 conhost.exe (88%), same picture (parent Code.exe held 1,894 handles).
- The smoking gun (thread stack + IRP analysis, crash #9): a sampled rg.exe (alive for 6+ minutes) had one thread blocked in
NtWriteFilewith a pendingIRP_MJ_WRITEon\FileSystem\Npfs, pipe name\uv\3812-19812— a libuv pipe (\\?\pipe\uv\<ptr>-<pid>), where 19812 = 0x4D64 = exactly the parent Code.exe/Node PID. I.e. the parent stopped draining the child's stdout; the pipe buffer filled; rg blocks forever in WriteFile. - The remaining threads of each blocked rg wait on
Timer2SynchronizationObjectwith ~15 ms cycles (16k-30k context switches each) — the source of 18,341 KTIMER2 objects (pool tags 52% IRTi, 47% TpWo; normal is a few hundred). 8,858 expired within one 10-second window ->KiTimer2Expiration -> KiProcessThreadWaitList>12 s on DISPATCH_LEVEL -> bugcheck 0x133 Arg1=1. - Third-party drivers ruled out: NVIDIA rollback did not change anything; no foreign driver on any DPC/ISR stack. A filesystem minifilter (
gameflt.sys, Xbox Gaming Install Filter) shows up in 2 of 6!analyzebuckets, but only as a contention victim of the parallel file-open storm — the flood itself is pure user-mode.
Suspected regression window
Claude Code had been used intensively on this machine for weeks without a single crash. First BSOD: 2026-07-07. npm publish dates: 2.1.201 on 07-03, 2.1.202 on 07-06 22:41 — the timing suggests a regression introduced around 2.1.202 (unproven; auto-update makes the exact installed version at first crash hard to reconstruct).
Relation to existing issues
- #62659 documents the missing per-command Job Objects /
SILENT_BREAKAWAY_OKdefeatingKILL_ON_JOB_CLOSE— that explains why nothing ever cleans these processes up. - #63043 (closed as duplicate of #62659) shows the identical rg+conhost accumulation (~2,000 processes).
- What is not documented anywhere yet: the leaked processes are live-blocked on the uv stdout pipe (parent stopped reading), and the resulting timer churn crashes the whole machine — a kernel bugcheck with full data loss, not just resource waste. microsoft/vscode#313167 shows the same 0x133 signature caused by Copilot's ripgrep scan and was misattributed to an audio driver there.
Expected behavior
- The Grep tool should kill (not abandon) rg children on timeout/abort — per-command Job Object with
KILL_ON_JOB_CLOSEand withoutSILENT_BREAKAWAY_OK(see #62659). - The parent should keep draining or explicitly close child stdout pipes; a closed pipe would at least let rg die with a write error instead of blocking forever.
- A cap on concurrent rg spawns would limit the blast radius.
Workaround
A PowerShell watchdog that force-kills rg.exe older than 60 s (panic mode above 100 concurrent instances) and orphaned conhost.exe. It demonstrably prevents the BSOD, but it is a band-aid, not a fix.
Available on request
Full WinDbg logs (!analyze -v, !dpcwatchdog, !timer, IRP details, process lists) from four full kernel dumps, plus the live monitoring CSVs.