SessionStart hooks defined in settings.json silently not invoked on Windows (v2.1.141)

Resolved 💬 5 comments Opened May 14, 2026 by FreedomBenjamin Closed Jun 12, 2026

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:

  1. The hook's primary command (node ...primer-sync.cjs) writes to a remote server on success. No request reaches the server from cold-starts on v2.1.141, ever.
  2. 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.
  3. 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

  1. 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 }
]
}
]
}
}
``

  1. Delete ~/.claude/hook-sentinel.log if present.
  2. Open a fresh PowerShell window. Run claude. Wait for the banner + prompt to fully render (~5s). Type a single character + Enter. Then /exit.
  3. 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.json shadowing. 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.140 user 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 sentinel powershell -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 with settings.local.json renamed 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.json and settings.local.json from 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.

View original on GitHub ↗

This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗