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)

Open 💬 0 comments Opened Jul 11, 2026 by claes-work

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

  1. 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.
  1. 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).
  2. The smoking gun (thread stack + IRP analysis, crash #9): a sampled rg.exe (alive for 6+ minutes) had one thread blocked in NtWriteFile with a pending IRP_MJ_WRITE on \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.
  3. The remaining threads of each blocked rg wait on Timer2SynchronizationObject with ~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.
  4. 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 !analyze buckets, 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_OK defeating KILL_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_CLOSE and without SILENT_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.

View original on GitHub ↗