Windows: Plugin stop hook paths break due to backslash expansion in CLAUDE_PLUGIN_ROOT
Bug
Stop hook paths on Windows have backslashes stripped when passed to bash.
Environment
- Claude Code: 2.1.39
- OS: Windows 11 Pro 10.0.26220
- Shell: Git Bash (/bin/bash)
- Plugin: ralph-loop (from claude-plugins-official marketplace)
What happens
Plugin hooks use ${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh in hooks.json. On Windows, Claude Code expands CLAUDE_PLUGIN_ROOT to a native Windows path with backslashes (e.g. C:\Users\rr\.claude\plugins\cache\claude-plugins-official\ralph-loop\2cd88e7947b7). When this is concatenated and passed to /bin/bash (Git Bash), the backslashes are interpreted as escape characters, breaking the path.
Expected behavior
Claude Code should normalize CLAUDE_PLUGIN_ROOT to forward slashes before invoking the command, or use the bash-compatible path format (e.g. C:/Users/rr/.claude/plugins/cache/...).
Reproduction
- Install the
ralph-loopplugin fromclaude-plugins-officialon Windows - The plugin's
hooks.jsonregisters a stop hook:
``json``
{
"hooks": {
"Stop": [{
"hooks": [{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/stop-hook.sh"
}]
}]
}
}
- The hook errors on session stop because the expanded path contains unescaped backslashes
Workaround
None available from the plugin side — the variable is expanded at runtime by Claude Code. The hook script itself works correctly when invoked with forward-slash paths:
# This works:
bash "C:/Users/rr/.claude/plugins/cache/.../hooks/stop-hook.sh"
# This breaks (what Claude Code generates):
bash "C:\Users\rr\.claude\plugins\cache\...\hooks\stop-hook.sh"
Notes
This likely affects all plugin hooks on Windows that use ${CLAUDE_PLUGIN_ROOT} in their command paths, not just ralph-loop.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗