[BUG] Hook command on Windows: backslashes in absolute paths are shell-mangled before execution

Resolved 💬 2 comments Opened Apr 29, 2026 by vhadianto Closed May 30, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report
  • [x] I am using the latest version of Claude Code

What's Wrong?

On Windows, when a PreToolUse (or any hook) command string contains a Windows absolute path with backslashes — e.g. pointing at a .ps1 script — the harness pipes the command through /usr/bin/bash, which interprets \U, \A, \g, etc. as unknown backslash escapes and silently strips the backslashes. The downstream process therefore receives a malformed path and fails to launch.

The hook still registers and fires (it shows up in --include-hook-events stream), but the spawned process can't find its target file and dies with exit 127. From the user's perspective the hook "doesn't work" with no obvious diagnostic.

What Should Happen?

Backslashes in hook command strings should be preserved verbatim on Windows, so absolute paths to scripts (.ps1, .cmd, .exe) work without escape acrobatics. Either:

  1. Use a Windows-aware shell to invoke the command (cmd.exe / pwsh) on Windows, OR
  2. Pre-escape backslashes before handing to bash, OR
  3. Document the bash-on-Windows behavior prominently in the hooks docs and recommend forward slashes.

Today's behavior makes the natural Windows JSON spelling ("C:\\Users\\me\\hook.ps1") silently break.

Error Messages/Logs

Running claude.exe -p "..." --include-hook-events --output-format=stream-json shows the hook firing and the spawned process's failure verbatim. With the hook command set to:

powershell.exe -ExecutionPolicy Bypass -File C:\\Users\\<user>\\probe.ps1 fired

The stream-json output reports:

{
  "type": "system",
  "subtype": "hook_response",
  "hook_name": "PreToolUse:Bash",
  "hook_event": "PreToolUse",
  "stderr": "The argument 'C:Users<user>probe.ps1' to the -File parameter does not exist. Provide the path to an existing '.ps1' file as an argument to the -File parameter.\r\n",
  "exit_code": 127,
  "outcome": "error"
}

Note the path the harness handed to powershell.exe: C:Users<user>probe.ps1 — every backslash has been stripped. Original config had C:\\Users\\<user>\\probe.ps1 (the standard JSON-escaped form).

Steps to Reproduce

  1. On Windows, save the following stub script as C:\Users\<user>\probe.ps1:

``powershell
"$args" | Out-File -FilePath "$env:TEMP\probe-fired.txt" -Encoding UTF8 -NoNewline
``

  1. Put this in ~/.claude/settings.json:

``json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "powershell.exe -ExecutionPolicy Bypass -File C:\\Users\\<user>\\probe.ps1 fired"
}
]
}
]
}
}
``

  1. Reload window (or start a fresh session) and have Claude run any Bash tool call.
  2. Observed: %TEMP%\probe-fired.txt is never created. The hook reports an error in stream-json output: PowerShell can't find a file at the de-escaped path (C:Users<user>probe.ps1).
  3. Workaround: change the command to use forward slashes — C:/Users/<user>/probe.ps1 — and the script runs as expected. Forward slashes survive bash escape-stripping unchanged.

Claude Code Version

2.1.123 (VSCode extension, bundled CLI)

Platform

Anthropic API (subscription)

Operating System

Windows Server 2025 Datacenter (10.0.26100). Reproduces on the VSCode extension's Native UI mode and via direct invocation of the bundled claude.exe -p ....

Terminal/Shell

VSCode integrated; harness shell is the bundled bash from Git for Windows / WSL-style /usr/bin/bash.

Additional Information

  • This is independent of issue #8985 (which is specifically about Notification / PermissionRequest subtype handlers missing). Per the source-code analysis in that thread, PreToolUse is supposed to work in the extension — and it does, the hook absolutely fires. The bug is purely in how the command string is dispatched on Windows.
  • The forward-slash workaround works because PowerShell's -File parameter accepts /-separated paths on Windows.
  • Suggested doc fix even before a code fix: add a "Windows: use forward slashes in hook command paths" note to the hooks reference, since it's the kind of footgun that costs hours to diagnose without --include-hook-events --output-format=stream-json (which is itself non-obvious).

View original on GitHub ↗

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