You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
SessionEnd viz hook fails on every exit on Windows (registered unconditionally, and bare bash resolves to WSL) #27
So on Windows and Linux, Claude Code spawns a shell at every session end purely to reach a *) branch that does nothing.
Cause 2 — bare bash resolves to the WSL launcher, which cannot read the Windows plugin path
"command": "bash" is resolved against PATH. On a default Windows install that is C:\Windows\System32\bash.exe — the WSL launcher, not Git Bash:
> (Get-Command bash).Source
C:\Windows\System32\bash.exe
> bash -c uname
Linux
${CLAUDE_PLUGIN_ROOT} expands to a Windows path, which the WSL shell consumes as escape sequences. Running the hook's exact command by hand:
> bash "C:\Users\<me>\.claude\plugins\cache\claude-plugins-official\datadog\0.7.17/viz/hooks/session_end.sh"
/bin/bash: C:Users<me>.claudepluginscacheclaude-plugins-officialdatadog0.7.17/viz/hooks/session_end.sh: No such file or directory
exit=127
The drive letter and every backslash are gone. The script is never found, so even the intended no-op cannot run.
Your diagnosis was right on both counts. The piece I'd add is whybash gets resolved against PATH in the first place: it's the args key. Supplying args is what puts a hook in exec form, where Claude Code resolves command as an executable and spawns it directly with no shell — which is how you end up at C:\Windows\System32\bash.exe. Dropping args and naming the script in the command string instead switches to shell form, which routes to sh -c on macOS/Linux and to Git Bash on Windows:
Shell form executes the file itself rather than handing it to an interpreter, so both scripts also need mode 755 — at 644 they exit 126.
Two notes beyond what you reported:
forward.sh has the identical bug. Same exec-form invocation, equally broken on Windows — it just fires on visualization tool calls rather than on every exit, so it wasn't as visible. It needs the opposite treatment from session_end.sh though: off macOS it isn't a no-op, it emits the "chart is at the sandbox URL" fallback to the model, so it has to keep working rather than stop being registered.
Cause 1 can't be fixed the way it's framed, and I think the premise doesn't hold. There's no OS conditional for hook registration. The if field takes permission-rule syntax and is only evaluated on tool events — "On other events, a hook with if set never runs." Putting if on SessionEnd would disable the hook on macOS too, the one platform it exists for. So a macOS-only hook can only be made cheap and silent, not unregistered. Once cause 2 is fixed it is exactly that: the shell starts, uname misses Darwin, the script exits 0 with no output. Cause 1 stops being a bug once cause 2 is.
Verified on a real Windows runner rather than reasoned about — the job hands the hook a native D:\a\... plugin root through C:\Program Files\Git\bin\bash.EXE, since Git Bash reports $PWD in Unix form and an ordinary Windows job would never see the path shape that caused this:
ok SessionEnd exits 0 on MINGW64_NT-10.0-26100
ok SessionEnd prints nothing
14 passed, 0 failed
I'm an outside contributor, so it's entirely up to the maintainers whether they take that shape — and per CONTRIBUTING this repo is regenerated from an internal source, so the change would need porting there regardless. Flagging it here in case it's useful to anyone else hitting the same error in the meantime.
Activity
ZayanKhan-12 commentedon Sep 14, 2026
Opened #29 for this: #29
Your diagnosis was right on both counts. The piece I'd add is why
bashgets resolved againstPATHin the first place: it's theargskey. Supplyingargsis what puts a hook in exec form, where Claude Code resolvescommandas an executable and spawns it directly with no shell — which is how you end up atC:\Windows\System32\bash.exe. Droppingargsand naming the script in the command string instead switches to shell form, which routes tosh -con macOS/Linux and to Git Bash on Windows:Shell form executes the file itself rather than handing it to an interpreter, so both scripts also need mode
755— at644they exit126.Two notes beyond what you reported:
forward.shhas the identical bug. Same exec-form invocation, equally broken on Windows — it just fires on visualization tool calls rather than on every exit, so it wasn't as visible. It needs the opposite treatment fromsession_end.shthough: off macOS it isn't a no-op, it emits the "chart is at the sandbox URL" fallback to the model, so it has to keep working rather than stop being registered.Cause 1 can't be fixed the way it's framed, and I think the premise doesn't hold. There's no OS conditional for hook registration. The
iffield takes permission-rule syntax and is only evaluated on tool events — "On other events, a hook withifset never runs." PuttingifonSessionEndwould disable the hook on macOS too, the one platform it exists for. So a macOS-only hook can only be made cheap and silent, not unregistered. Once cause 2 is fixed it is exactly that: the shell starts,unamemissesDarwin, the script exits 0 with no output. Cause 1 stops being a bug once cause 2 is.Verified on a real Windows runner rather than reasoned about — the job hands the hook a native
D:\a\...plugin root throughC:\Program Files\Git\bin\bash.EXE, since Git Bash reports$PWDin Unix form and an ordinary Windows job would never see the path shape that caused this:I'm an outside contributor, so it's entirely up to the maintainers whether they take that shape — and per CONTRIBUTING this repo is regenerated from an internal source, so the change would need porting there regardless. Flagging it here in case it's useful to anyone else hitting the same error in the meantime.