Hook process freezes at startup on Windows (thread Suspended, 0 CPU, never resumed) -> hangs the turn; kills subagents permanently
Hook process freezes at startup on Windows (thread Suspended, 0 CPU, never resumed) → hangs the turn; kills subagents permanently
Environment
- Claude Code: 2.1.210
- OS: Windows 11 Pro (26200)
- Shell: PowerShell + Git Bash
- Configured hooks: PreToolUse, PostToolUse, Stop, SubagentStop, UserPromptSubmit (Python scripts)
Summary
A hook child process (python.exe) is spawned but never runs its first instruction — its
main thread stays in the kernel Suspended wait state (suspend_count = 1, exactly zero CPU
time). Claude Code waits for the hook, logs timed out — output discarded, but neither
resumes nor kills the process, so the turn hangs. Intermittent, Windows-specific.
For the main session this manifests as a turn hanging 30–60 min. For subagents it is worse:
the subagent's turn is abandoned, the tool never returns a tool_result, and the subagent dies
permanently — the parent session spins forever with a result that will never arrive.
Evidence (measured, not guessed)
Frozen hook process, characterised with a debugger:
- main thread
WaitReason = Suspended,suspend_count = 1 - exactly 0 ms CPU (control: a process that started normally and then blocked on stdin always
shows 15–31 ms, 12/12 — so 0 ms means it never executed a single instruction)
- survives its 2–3 s timeout budget indefinitely (observed 46–127 min)
- a single
ResumeThread()(previous suspend_count = 1) → the process finishes and exits in <1 s.
Reproduced 5×.
- 120 spawns of the same hook without Claude Code → zero freezes (31 ms each) → not the script.
- CC waits for such a hook and hangs the whole turn — including on a plain
Read, not only shell.
Subagent impact, measured across 194 subagent runs (one week):
- 7 subagents died (3.6%) — transcript ends mid-
tool_use(a tool was requested,tool_result
never arrived), no further lines for 89–173 min while the parent session was still alive.
- Two deaths fall inside a hook-freeze window recorded independently in our watchdog log.
- Correlates in time: subagent deaths start the same day the hook-freeze log starts.
- Not model/variant related: dies on both Opus and Haiku subagents; last tool before death is a
plain Read/Grep/WebFetch with no pattern.
Ruled out (in case it helps narrow it)
- Not the hook script (120 spawns outside CC → 0 freezes).
- Not antivirus alone (no Defender events for
python.exe; spawn timing stable ~65 ms when it works). - A libuv
uv_spawnCREATE_SUSPENDEDrace was our first hypothesis and we DISPROVED it in
libuv/src/win/process.c: CREATE_SUSPENDED is set only for UV_PROCESS_DETACHED, and the
job-object assignment sits in a mutually-exclusive branch. Hooks are not detached, so that path
does not apply. Mechanism remains unknown — we did not want to ship you a guessed cause.
Why this matters / severity
- Any user with hooks is exposed on every tool call (we run several synchronous hooks per tool → a
few hundred process spawns per subagent run). A user with zero hooks never hits it, which likely
explains why it is rarely reported.
- The subagent case is silent data loss: work is lost, the session spins forever, and there is **no
timeout and no error** surfaced to the parent — the Agent tool never returns and never fails.
Two concrete asks for the platform
- A timeout/abort on a hung hook: if a hook exceeds its budget, CC should resume-or-kill the
process (a single ResumeThread demonstrably unblocks it) instead of waiting indefinitely.
- A subagent that loses its turn to a hung hook should fail the Agent tool (or retry), not leave
the parent spinning with no result and no error.
Our current workaround (in case useful as a reference)
A watchdog that scans for python.exe hook processes from our hooks dir with 0 CPU and age past a
grace window, and calls ResumeThread() on the suspended thread. The turn unfreezes in ~3–5 s. This
is a symptom mitigation; the root cause is inside CC's hook-spawn path on Windows.
Repro (best-effort — it is intermittent)
- Windows, several synchronous hooks registered per tool event (Python).
- Drive tool-heavy work, especially subagents (30–70 tool calls each → hundreds of hook spawns).
- Occasionally a hook
python.exewill sit atsuspend_count=1, 0 CPU; the turn hangs. For a
subagent, the run never returns.