Windows: ${CLAUDE_PLUGIN_ROOT} expansion strips backslashes in hook commands

Resolved 💬 3 comments Opened Feb 7, 2026 by aka-kool Closed Feb 11, 2026

Bug Description

On Windows (Git Bash/MSYS2), the ${CLAUDE_PLUGIN_ROOT} variable expansion in plugin hook commands strips all backslashes from the path, causing hook scripts to fail silently.

Environment

  • OS: Windows 11 (Git Bash / MSYS2)
  • Claude Code: Latest (via Volta)
  • Shell: /bin/bash (Git Bash C:\Program Files\Git\usr\bin\bash.exe)

Steps to Reproduce

  1. Install any marketplace plugin that uses ${CLAUDE_PLUGIN_ROOT} in hooks.json (e.g., claude-supermemory)
  2. The plugin's hooks.json contains:

``json
{
"command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/context-hook.cjs\"",
"timeout": 30
}
``

  1. Start a new Claude Code session (triggers SessionStart hook)

Expected Behavior

${CLAUDE_PLUGIN_ROOT} should expand to a valid path that bash can resolve, e.g.:

node "C:\Users\user\.claude\plugins\cache\supermemory-plugins\claude-supermemory\0.0.1/scripts/context-hook.cjs"

Or preferably with forward slashes:

node "C:/Users/user/.claude/plugins/cache/supermemory-plugins/claude-supermemory/0.0.1/scripts/context-hook.cjs"

Actual Behavior

All backslashes are stripped during variable expansion. From the debug log:

[DEBUG] Hooks: Adding stderr to async_hook_6804: /bin/bash: C:Usersaasare.claudepluginscacheclaude-...
[DEBUG] Hooks: Checking hook async_hook_6804 (SessionStart:startup) - attachmentSent: false, stdout length: 0
[DEBUG] Hooks: Skipping hook async_hook_6804 - already delivered/sent or no stdout

The path C:\Users\aasare\.claude\plugins\cache\supermemory-plugins\claude-supermemory\0.0.1 becomes C:Usersaasare.claudepluginscachesupermemory-pluginsclaude-supermemory0.0.1 (every \ removed).

Proof That Bash Is Not The Problem

Direct testing confirms bash handles backslash paths correctly:

const { spawn } = require('child_process');
const bashPath = 'C:\Program Files\Git\usr\bin\bash.exe';
const pluginRoot = 'C:\Users\aasare\.claude\plugins\cache\supermemory-plugins\claude-supermemory\0.0.1';
const cmd = `node "${pluginRoot}/scripts/context-hook.cjs"`;

const child = spawn(bashPath, ['-c', cmd]);
// ✅ Works perfectly - produces correct stdout output

The backslash stripping happens during Claude Code's ${CLAUDE_PLUGIN_ROOT} text substitution, before the command reaches bash.

Additional Issues Observed

  • Hook timeout: 30 (seconds) in hooks.json is registered as 600000ms (10 minutes) in the debug log - the timeout value appears to be misinterpreted.

Suggested Fix

Normalize ${CLAUDE_PLUGIN_ROOT} to use forward slashes on Windows before injecting into hook command strings:

// Before injecting into the command string:
const normalizedRoot = pluginRoot.replace(/\/g, '/');
const resolvedCmd = hookConfig.command.replace('${CLAUDE_PLUGIN_ROOT}', normalizedRoot);

This would produce paths like C:/Users/aasare/.claude/... which work correctly in both bash and Node.js on Windows.

Impact

This bug silently breaks all plugin hooks that use ${CLAUDE_PLUGIN_ROOT} on Windows. Hooks fail with no stdout, and Claude Code skips them. Users see no error in the UI - the plugin simply appears non-functional.

View original on GitHub ↗

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