[BUG] Windows: Claude Code never executes configured hook commands — hook runner invokes `git` instead and silently swallows the failure

Open 💬 0 comments Opened Jul 1, 2026 by mee03201216-gif

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

Environment

  • Claude Code version: 2.1.197
  • Affected entrypoints: Both reproduce identically
  • Desktop app for Windows (entrypoint: "claude-desktop"), installed via WinGet (Anthropic.Claude)
  • Standalone CLI (entrypoint: "cli"), installed via the official native installer (irm https://claude.ai/install.ps1 | iex) to ~/.local/bin/claude.exe
  • OS: Windows 11 Home, build 10.0.26200 (x64)
  • Git: 2.53.0.windows.1, standard install at C:\Program Files\Git (both bin\bash.exe and usr\bin\bash.exe present at their expected locations — this is a normal, unmodified Git for Windows layout, not a broken/nonstandard install)
  • Shell used to launch Claude Code: Windows PowerShell 5.1

Summary

On this Windows machine, no hook configured in ~/.claude/settings.json ever actually executes — regardless of the hook event, the command content, or whether the command is wrapped in cmd /c "..." or given as a bare path. Claude Code's own session transcript confirms it attempts to run the hook (the event fires and is logged), but the process that actually runs is not the configured command: it appears to invoke plain git instead, which prints git's usage/help banner and exits with code 1. Claude Code classifies this as a "non-blocking" hook error and silently proceeds, so the user sees no indication anything is wrong — the hook simply never has any effect.

This makes every hook-based integration on Windows non-functional (tested with a third-party governance tool, but the minimal repro below has nothing to do with that tool — it's a single trivial batch file).

Steps to reproduce (minimal repro)

  1. Create a trivial batch file, e.g. C:\Users\<you>\probe.cmd:

``bat
@echo off
echo PROBE-FIRED %DATE% %TIME% >> "%USERPROFILE%\probe.log"
``

  1. In ~/.claude/settings.json, configure it as the only hook:

``json
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{ "type": "command", "command": "C:\\Users\\<you>\\probe.cmd" }
]
}
]
}
}
`
(Also tested wrapped as
"command": "cmd /c \"C:\\Users\\<you>\\probe.cmd\""` — identical result.)

  1. Delete probe.log if it exists.
  2. Start a brand-new Claude Code session (not resumed) — reproduced in both the Desktop app and claude CLI.
  3. Send any prompt, e.g. "hello".
  4. Check %USERPROFILE%\probe.log.

Expected behavior

probe.log gets a new line each time a prompt is submitted, since the batch file does nothing but append to it.

Actual behavior

probe.log is never created. The session's transcript JSONL (~/.claude/projects/.../<session-id>.jsonl) contains a hook_non_blocking_error attachment for the UserPromptSubmit event whose command field correctly matches the configured hook, but whose stdout is git's bare usage banner and exitCode is 1 — i.e., the configured command was never actually run; something else (apparently git with no/invalid arguments) ran instead.

Example attachment from a real CLI session (entrypoint: "cli", sessionId redacted), reproduced verbatim except for truncating the (unmodified) git usage text for brevity:

{
  "attachment": {
    "type": "hook_non_blocking_error",
    "hookName": "UserPromptSubmit",
    "hookEvent": "UserPromptSubmit",
    "stderr": "Failed with non-blocking status code: No stderr output",
    "stdout": "usage: git [-v | --version] [-h | --help] [-C <path>] [-c <name>=<value>]\n           [--exec-path[=<path>]] ... (full git top-level usage banner) ...",
    "exitCode": 1,
    "command": "C:\\Users\\<you>\\probe.cmd",
    "durationMs": 381
  },
  "type": "attachment",
  "entrypoint": "cli",
  "cwd": "C:\\Users\\<you>\\<project>",
  "version": "2.1.197"
}

The identical attachment (same stdout, exitCode: 1) was produced in a separate test run through the Desktop app (entrypoint: "claude-desktop"), and in a third run with the hook command written as cmd /c "..." instead of a bare path — ruling out both the entrypoint and the command-quoting style as the variable. Only one hook was configured in each test (all others removed), so there is no ambiguity about which hook produced this result.

What Should Happen?

What we ruled out

  • Not a broken/nonstandard Git for Windows installgit --version, git.exe location, and both bin\bash.exe / usr\bin\bash.exe are present exactly where Git for Windows normally puts them.
  • Not a settings.json syntax problem — the JSON is valid (parses fine), and the error attachment's own command field proves Claude Code read the correct, intended command string.
  • Not specific to the Desktop app — reproduced identically via the standalone CLI (entrypoint: "cli"), installed separately via the official native installer.
  • Not specific to cmd /c quoting — reproduced identically with a bare, unwrapped path as the command value.
  • Not a fresh-session/caching issue — reproduced across multiple brand-new sessions (not resumed) in both interfaces.
  • Not related to a UTF-8 BOM in hook stdin (a separate, real issue we found and worked around upstream of this one, in a third-party tool's own hook gateway) — this repro is a plain .cmd batch file with no JSON parsing involved at all, so BOM handling is irrelevant here.

Impact

Because the failure is classified as "non-blocking" and produces no visible error to the user, any hook-based tool on Windows silently does nothing, while appearing (from settings.json) to be correctly configured. For security/governance tooling built on Claude Code hooks specifically, this means enforcement silently never happens — a user could reasonably believe a hook-based guardrail is active when it is not.

Error Messages/Logs

Steps to Reproduce

Suggested next steps for triage

  • Check the Windows-specific hook command execution path for anything that shells out to git (e.g., as part of Git Bash detection/selection logic for the Bash tool) that might be getting invoked in place of the actual hook command, or that the hook command is being incorrectly appended as an argument to a git invocation.
  • A claude doctor or verbose/debug flag that surfaces the exact argv/CreateProcess call Claude Code makes for a hook on Windows would make this much faster to diagnose further.

Happy to provide the full (unredacted) transcript JSONL lines, settings.json, or run further isolation tests on request.

Claude Model

Sonnet (default)

Is this a regression?

Yes, this worked in a previous version

Last Working Version

2.1.197

Claude Code Version

2.1.197

Platform

Anthropic API

Operating System

Windows

Terminal/Shell

PowerShell

Additional Information

_No response_

View original on GitHub ↗