claude-code - 💡(How to fix) Fix [BUG] Hook command on Windows: backslashes in absolute paths are shell-mangled before execution [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#54640Fetched 2026-04-30 06:40:06
View on GitHub
Comments
1
Participants
2
Timeline
5
Reactions
0
Author
Timeline (top)
labeled ×4commented ×1

Error Message

Error Messages/Logs

"outcome": "error" 4. 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).

Root Cause

  • 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).

Fix Action

Fix / Workaround

  1. On Windows, save the following stub script as C:\Users\<user>\probe.ps1:
    "$args" | Out-File -FilePath "$env:TEMP\probe-fired.txt" -Encoding UTF8 -NoNewline
  2. Put this in ~/.claude/settings.json:
    {
      "hooks": {
        "PreToolUse": [
          {
            "matcher": "Bash",
            "hooks": [
              {
                "type": "command",
                "command": "powershell.exe -ExecutionPolicy Bypass -File C:\\Users\\<user>\\probe.ps1 fired"
              }
            ]
          }
        ]
      }
    }
  3. Reload window (or start a fresh session) and have Claude run any Bash tool call.
  4. 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).
  5. 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.
  • 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).

Code Example

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

---

{
  "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"
}

---

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

---

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

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report
  • 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:
    "$args" | Out-File -FilePath "$env:TEMP\probe-fired.txt" -Encoding UTF8 -NoNewline
  2. Put this in ~/.claude/settings.json:
    {
      "hooks": {
        "PreToolUse": [
          {
            "matcher": "Bash",
            "hooks": [
              {
                "type": "command",
                "command": "powershell.exe -ExecutionPolicy Bypass -File C:\\Users\\<user>\\probe.ps1 fired"
              }
            ]
          }
        ]
      }
    }
  3. Reload window (or start a fresh session) and have Claude run any Bash tool call.
  4. 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).
  5. 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).

extent analysis

TL;DR

Use forward slashes in hook command paths on Windows to avoid backslash stripping by the bash shell.

Guidance

  • Verify that the issue is resolved by checking the stream-json output for the correct path being passed to the spawned process.
  • Update the command string in the settings.json file to use forward slashes instead of backslashes for Windows absolute paths.
  • Consider documenting the recommended use of forward slashes in hook command paths on Windows in the hooks reference to prevent similar issues in the future.
  • If a code fix is preferred, investigate using a Windows-aware shell to invoke the command or pre-escaping backslashes before handing to bash.

Example

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

Notes

This workaround takes advantage of PowerShell's ability to accept forward slashes in paths on Windows. A more permanent fix may involve modifying the Claude Code to handle backslashes correctly on Windows.

Recommendation

Apply the workaround by using forward slashes in hook command paths on Windows, as it is a simple and effective solution that can be implemented immediately.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix [BUG] Hook command on Windows: backslashes in absolute paths are shell-mangled before execution [1 comments, 2 participants]