SessionStart hooks defined in settings.json silently not invoked on Windows (v2.1.141)
Summary
On Claude Code v2.1.141 (Windows 10/11, PowerShell), a SessionStart hook registered in ~/.claude/settings.json is never invoked — even on a fresh claude cold-start in a brand new terminal. The hook entry is well-formed and the referenced script runs perfectly when invoked directly. A sentinel hook with a completely different command (a one-line Add-Content via powershell -Command) registered in the same array also never fires.
A separate Windows user on v2.1.140 running the same installer (same backslash-path hook command shape) has hook fires landing on our server successfully — confirmed via server-side log of two successful sync requests at 2026-05-13 20:59:57 UTC while running v2.1.140. That user has since auto-updated to v2.1.141; we'll have an in-flight natural-experiment data point on whether v2.1.141 breaks hooks for him too once he cold-starts.
This appears to be a Windows-specific regression in hook dispatch introduced in v2.1.141, not a per-hook misconfiguration.
Environment
| | |
|---|---|
| OS | Windows 10/11 |
| Shell | PowerShell |
| Claude Code (broken) | 2.1.141 (Claude Code) |
| Claude Code (working) | 2.1.140 (Claude Code) |
| Node | 24.x |
| ~/.claude/settings.json | well-formed, contains hooks.SessionStart array with one entry |
| ~/.claude/settings.local.json | exists with only permissions (no hooks block) |
Expected behavior
On claude cold-start (new PowerShell window), the SessionStart hook commands should be invoked. Stdout from those commands should be captured as system-reminder context for the model.
Actual behavior
No SessionStart hook command runs. Confirmed by three independent signals:
- The hook's primary command (
node ...primer-sync.cjs) writes to a remote server on success. No request reaches the server from cold-starts onv2.1.141, ever. - A second SessionStart hook entry added for diagnosis — a one-line
powershell -Command \"Add-Content -Path \$env:USERPROFILE\.claude\hook-sentinel.log -Value ...\"— also does not create its sentinel file. - The system-reminder context Claude receives at session start does NOT include the expected output that the primer-sync script emits.
The exact same settings.json produces all three on a v2.1.140 install.
Reproduction
- Add to
~/.claude/settings.json:
``json``
{
"hooks": {
"SessionStart": [
{
"matcher": "*",
"hooks": [
{ "type": "command",
"command": "powershell -Command \"Add-Content -Path $env:USERPROFILE\.claude\hook-sentinel.log -Value ([DateTime]::UtcNow.ToString('o') + ' fired')\"",
"timeout": 5000 }
]
}
]
}
}
- Delete
~/.claude/hook-sentinel.logif present. - Open a fresh PowerShell window. Run
claude. Wait for the banner + prompt to fully render (~5s). Type a single character + Enter. Then/exit. - Inspect
~/.claude/hook-sentinel.log.
Expected: file exists with one UTC timestamp line.
Observed (on v2.1.141): file does not exist.
What we ruled out
settings.local.jsonshadowing. Renamed it out (mv ~/.claude/settings.local.json ~/.claude/settings.local.json.bak), cold-started, hook still didn't fire. Restored after the test.- Backslash path in hook command (related to #54640). The working
v2.1.140user has the identical backslash-path command shape (node C:\Users\<user>\.claude\primer\bin\primer-sync.cjs) and hooks fire successfully. - Hook timeout too short. Sentinel had 5000ms for a one-line
Add-Content; PowerShell cold-start completes in <2s. - Session terminated before hook init. Cycle was banner-visible → user input →
/exit, not insta-exit. - Path/script issue. Both
node ...primer-sync.cjs(the original) and the sentinelpowershell -Command Add-Content ...(a completely different shell, command, and program) fail to fire. - Network / downstream issue. The sentinel hook never touches a network — it just writes a local file — and still doesn't fire.
Related (but distinct) existing issues
- #54640 — backslash mangling: ruled out here (manual invocation works; another Windows user with backslash paths works fine on v2.1.140)
- #50243 — Pre/PostToolUse hooks ignored with
settings.local.json-only: similar shape but different event types and different mechanism (we tested withsettings.local.jsonrenamed out and the bug persists) - #21468 — plugin SessionStart hooks fail on Windows: this report is for user-settings hooks, not plugin, but possibly the same underlying defect
Diagnostic data available
Happy to supply on request:
- Doctor-script output from the affected machine (all four checks green: hook registered, script exists, manifest fresh after manual sync, live sync passes)
- Server-side log proving zero hook-initiated requests across an 8-day window from the affected machine
- Full
settings.jsonandsettings.local.jsonfrom both the broken (v2.1.141) and working (v2.1.140) installs for diff
Workaround
Affected users run node ~/.claude/primer/bin/primer-sync.cjs manually as a shell alias when fresh data is needed. Functional but defeats the "silent SessionStart hook" design intent — and is invisible to the user (they have no way to know their hook isn't firing without server-side instrumentation).
Severity
Low individual impact (manual workaround exists), but high invisibility — affected users don't know their hooks aren't firing unless someone tells them. Several internal tools that rely on SessionStart hooks for context-loading fail silently for users on this version.
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗