Hook stdout incorrectly routed to stderr when using Git Bash on Windows
Description
When running Claude Code from Git Bash on Windows, hook commands that write to stdout have their output incorrectly appear on stderr. This causes Claude Code to display "hook error" messages even though hooks complete successfully with exit code 0.
Environment
- OS: Windows 11
- Shell: Git Bash (MINGW64)
- Node.js: v22.14.0
- Claude Code: Latest
Root Cause
Git Bash's pipe implementation on Windows has a compatibility issue with Node.js's stream handling. When stdin is provided via pipe (echo | node), stdout writes get routed to stderr.
Reproduction
# In Git Bash on Windows:
# stdout goes to stderr (BUG)
echo '{}' | node -e "console.log('test')" 1>/tmp/stdout.log 2>/tmp/stderr.log
cat /tmp/stdout.log # Empty
cat /tmp/stderr.log # Contains "test"
# These work correctly:
node -e "console.log('test')" < /tmp/input.json # File redirect - OK
node -e "console.log('test')" <<< '{}' # Here-string - OK
Impact
All hooks that write JSON responses to stdout appear to fail from Claude Code's perspective, even though they execute correctly.
Suggested Fixes
- Check stderr for valid JSON when exit code is 0: If a hook exits successfully but has JSON on stderr and nothing on stdout, treat the stderr content as the response.
- Use file-based IPC on Windows: Instead of piping, write input to a temp file and read output from a temp file.
- Use here-string or file redirect: Instead of
echo | node hook.mjs, usenode hook.mjs <<< "$input"or file redirection.
Test Matrix
| Input Method | Shell | stdout Destination |
|--------------|-------|-------------------|
| echo \| node | Git Bash | stderr (BUG) |
| node < file | Git Bash | stdout (OK) |
| node <<< str | Git Bash | stdout (OK) |
| echo \| node | PowerShell | stdout (OK) |
| echo \| node | cmd.exe | stdout (OK) |
Additional Context
This appears to be a known compatibility issue between Git Bash (MINGW64) and Node.js on Windows. The file descriptors are correctly numbered inside Node.js, but the shell's pipe routing is incorrect.
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗