[BUG] Bash tool: all commands fail with Bad file descriptor on Windows (Git Bash stdout broken)
Bug Description
Every command run through the Bash tool fails with exit code 1. Stdout file descriptor (fd 1) is broken - bash cannot write any output. PowerShell commands work fine through the same tool.
This is not the same issue as #18469 (which was silent empty stdout with exit code 0). This produces an explicit Bad file descriptor error on fd 1.
Environment
- Claude Code version: 2.1.45
- Platform: Windows 11 (MINGW64_NT-10.0-26200 3.6.4-b9f03e96.x86_64)
- Shell: Git Bash (C:\Program Files\Git\usr\bin\bash.exe)
- Sandbox: Disabled (sandbox.enabled: false)
- No CLAUDE_CODE_SHELL or CLAUDE_CODE_GIT_BASH_PATH env vars set
Reproduction
Any bash command fails:
Bash(date) -> Exit code 1 (no output)
Bash(echo hello) -> Exit code 1 (no output)
Bash(pwd) -> Exit code 1 (no output)
Bash(env | sort) -> Exit code 2 (no output)
PowerShell wrapper works fine:
Bash(powershell -Command Get-Date) -> Tuesday, February 17, 2026 3:46:15 PM
Root Cause (diagnosed)
Running bash directly via PowerShell with stderr captured reveals the error:
powershell
& 'C:\Program Files\Git\bin\bash.exe' -c 'echo hello' 2>&1
Output:
/usr/bin/bash: line 1: echo: write error: Bad file descriptor
The stdout pipe that Claude Code creates for capturing bash output is not valid from MSYS2/Git Bash's perspective. Bash builtins (echo, pwd) fail because fd 1 is broken. PowerShell works because it handles Windows pipe handles natively.
Diagnostic Steps Taken
- Confirmed PATH is correct - C:\Program Files\Git\usr\bin is on PATH, bash binary exists and runs
- Removed ~/.bashrc - no change, still broken (rules out #18469 root cause)
- Checked system bashrc/profile - standard Git for Windows defaults, nothing unusual
- No CLAUDE env vars - CLAUDE_CODE_SHELL and CLAUDE_CODE_GIT_BASH_PATH are not set
- Git Bash works normally outside of Claude Code (user runs it daily)
Expected Behavior
Bash commands should execute and return stdout, as they do in a normal Git Bash terminal.
Likely Cause
The child process spawning mechanism is not passing stdout pipe handles in a way that MSYS2's POSIX fd layer can use. This may be related to how child_process.spawn() or similar Node.js APIs create pipes on Windows - the Windows HANDLEs are not being translated to valid POSIX file descriptors by MSYS2's crt0 initialization.
Related Issues
- #18469 - Bash tool doesn't capture stdout from shell scripts on Windows (different symptoms: silent empty stdout, exit 0; fixed in v2.1.27)
- #20453 - CLAUDE_CODE_SHELL not respected on Windows (closed as dup)
- #20034 - Hook stdout routed to stderr in Git Bash (closed as dup of #18469)
- #25558 - CLAUDE_CODE_SHELL ignored on Windows (open)
11 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Confirmed regression in 2.1.45, pin versions and the difference is in 45.
This is not a duplicate of those issues. Here's why:
| | #18469 / #18748 / #19057 (fixed v2.1.27) | This issue (#26413) |
|---|---|---|
| Symptom | Silent empty stdout, exit code 0 | echo: write error: Bad file descriptor, exit code 1 |
| Root cause | .bashrc -> set -o onecmd in shell snapshot | stdout fd itself is broken |
| .bashrc involved? | Yes - removing it was the workaround | No - tested by removing .bashrc, still broken |
| Version | v2.1.0-v2.1.26 | v2.1.45 (includes the v2.1.27 fix) |
Likely regression: v2.1.45 changed bash stdio from pipes to file descriptors (stdio: [pipe, fd, fd] where fd = openSync(path, a)). MSYS2/Git Bash cannot inherit a Windows file HANDLE opened via Node.js openSync() as a valid POSIX file descriptor - its crt0 initialization doesn't translate the HANDLE, so fd 1 is invalid from bash's perspective. This is why echo (a bash builtin writing to fd 1) fails with Bad file descriptor.
The previous pipe-based stdio worked because Node.js pipes use proper Windows named pipes that MSYS2 can handle. The file-based approach works for native Windows programs (PowerShell) but not for MSYS2 programs.
Repro: Any bash command on Windows with v2.1.45:
Having this as well, noticed SSH commands breaking, rolling back to 2.1.44 fixes it for now.
See: #26418
Downgrade (pwsh):
& ([scriptblock]::Create((irm https://claude.ai/install.ps1))) 2.1.44Can confirm this on Windows 10 Pro (10.0.19045) + Git Bash + Claude Code.
Additional diagnostic finding that narrows down the root cause:
Standalone .exe binaries work through the Bash tool, but builtins and pipes don't:
| Command | Result |
|---|---|
|
node -e "console.log('test')"| Works ||
git --version| Works ||
git -C /path ls-files| Works (returned 683 files) ||
echo hello| Exit code 1, no output ||
whoami| Exit code 1, no output ||
ls| Exit code 2, no output ||
echo hello \| wc -l| Exit code 1, no output ||
printf "hello"| Exit code 1, no output |Key proof that bash itself is fine: Running bash commands through Node's
execSyncwith the Git Bash shell explicitly specified works perfectly:Also confirmed: no
~/.bashrc,~/.bash_profile, or~/.profileexists — the issue is purely in how the Bash tool spawns the process, not shell init.Workaround: route anything that needs shell features through
node -einstead of running bash commands directly.Problem is that claude self-updates to .45. I went for 2.1.37 and used /config within claude to set the
Auto-update channelpolicy to stable (which is 2.1.37 currently).Confirming this on Windows 11 with WSL bash (not just Git Bash)
Adding a data point — this also affects WSL bash (C:\Windows\System32\bash.exe), not only Git Bash/MSYS2. Same symptoms:
all commands producing stdout return exit code 1 with no output. Commands with no output succeed.
Environment:
What I tried (none helped):
What fixed it: Rolling back to v2.1.37 via winget.
Note for others rolling back: The native install auto-updater is aggressive — it reverted my first rollback (to 2.1.44)
back to 2.1.45 immediately. To make the rollback stick, set "autoUpdatesChannel": "stable" in ~\.claude\settings.json
before reinstalling the older version:
winget install Anthropic.ClaudeCode --version 2.1.37 --force
Has anyone confirmed if this is still an issue in 2.1.47?
Confirmed fixed in v2.1.47.
CHANGELOG (v2.1.47)
Local verification (Windows 11 + Git Bash)
echo,date,printf— all return output correctlyset -euo pipefail— no longer fail silentlyThe issue reported here is resolved.
Sorry about this. This issue was fixed in Claude Code v2.1.47.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.