Bash tool returns empty output when executing shell script files on Windows (MINGW64)
Description
On Windows with MINGW64 (Git Bash), the Bash tool returns empty output ("No content") when executing shell script files. This affects common commands like docker, pnpm, and npm which use shell script wrappers on Windows.
Environment
- OS: Windows 11 (Build 26200.7623)
- Shell: MINGW64 (Git Bash)
- Claude Code version: Latest
Steps to Reproduce
- Create a simple shell script:
cat > /tmp/test.sh << 'EOF'
#!/bin/sh
echo "hello world"
EOF
chmod +x /tmp/test.sh
- Execute the script via Bash tool:
/tmp/test.sh
# Returns: (No content)
bash /tmp/test.sh
# Returns: (No content)
sh /tmp/test.sh
# Returns: (No content)
- Compare with inline execution:
sh -c 'echo "hello world"'
# Returns: hello world (works correctly)
Affected Commands
| Command | Type | Result |
|---------|------|--------|
| docker | Shell script wrapper | (No content) |
| pnpm | Shell script wrapper | (No content) |
| ./script.sh | Direct script execution | (No content) |
| bash script.sh | Explicit bash invocation | (No content) |
| sh script.sh | Explicit sh invocation | (No content) |
Working Alternatives
| Command | Type | Result |
|---------|------|--------|
| docker.exe | Windows executable | ✓ Works |
| pnpm.cmd | CMD wrapper | ✓ Works |
| sh -c 'inline command' | Inline execution | ✓ Works |
| . script.sh (source) | Current shell | ✓ Works |
| eval "$(cat script.sh)" | Eval | ✓ Works |
Root Cause Analysis
The issue appears to be in how Claude Code's Bash tool captures stdout when:
- A new subprocess is spawned to execute a script file
- The environment is MINGW64 on Windows
Inline commands (sh -c '...') work because they don't spawn a separate process for file execution. Direct .exe and .cmd files work because they bypass the shell script layer.
Impact
This significantly impacts Windows users who rely on common tools that use shell script wrappers:
- Docker CLI
- Node.js package managers (pnpm, npm scripts)
- Any npm-installed global CLI tools
Suggested Fix
Consider one of these approaches:
- Detect MINGW64 environment and adjust subprocess handling
- Use different output capture mechanism for script file execution on Windows
- Document workaround for Windows users (use
.exe/.cmdsuffixes)
Workarounds
Users can work around this by:
- Using explicit
.exeor.cmdsuffixes:docker.exe,pnpm.cmd - Running commands through WSL:
wsl -d Ubuntu -e sh -c "docker ps" - Using source or eval for simple scripts
41 Comments
Found 2 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Workaround Solution
I've implemented a working workaround using
BASH_ENVto automatically load shell functions that redirect commands to their.exe/.cmdequivalents.Setup
1. Create the workaround script:
2. Add to Claude Code settings (
~/.claude/settings.json):3. (Optional) Set Windows environment variable for other tools:
How it works
BASH_ENVis sourced by bash for non-interactive shells.exeor.cmdversions directly, bypassing shell script wrappersVerified working
After implementing this workaround:
docker version✓docker ps✓pnpm --version✓npm --version✓This is a temporary solution until the root cause is fixed in Claude Code.
Updated: Dynamic Workaround Solution (v2)
The previous static workaround required manual updates. Here's an improved dynamic detection approach that automatically handles all affected commands.
How it works
.exe/.cmdequivalentsSetup
1. Create the workaround script:
Save to
~/.claude/shell-fixes/mingw64-workarounds.sh:2. Add to Claude Code settings (
~/.claude/settings.json):Detected commands (example)
On my system, this automatically detected 35 commands:
docker,docker-composenpm,npx,pnpm,yarn,tsc,ts-node,eslint,firebase,expo, etc.Maintenance
mingw64-refresh-wrappersto rescan__SCAN_DIRSarrayI'm also having this issue.
@Daichi-Kudo thank you for sharing this script.
I had to modify your script to fix an escape error to replace "'s|\|/|g'" with "'s|\\|/|g'". I'm not sure why, but maybe it was related to the markdown editor on GitHub.
I hope they make an official fix, because you don't really notice this bug right away (especially if you're a new user to Claude), or alternatively it just confuses new users on Windows who give up trying to learn Claude.
set env
"SHELLOPTS": "braceexpand"
in claude code settings.json
There are too many bugs in the latest version, especially when using Claude Code in Windows environments. It is recommended to use version 2.1.7 @Daichi-Kudo
How can we get Anthropic's attention on this? I fear they don't know that all Windows users are impacted by this.
My workaround was to switch to WSL (Windows Subsystem Linux) with Ubuntu to run Claude Code, and use the "WSL Extension" within Visual Code to connect to "WSL:Ubuntu" which allows you to run the WSL terminal inside Visual Code and Claude was able to connect to the IDE installed Claude extension. Which is pretty impressive that everything seems to be working.
If you're using Docker, there is a WSL integration feature on Docker Desktop for Windows to enable WSL support. I haven't tried it yet, but some of my projects require it, but I'm hopeful it'll work.
I was able to run the latest Claude Code and test output from Bash commands from a WSL terminal in Visual Code.
<img width="1749" height="637" alt="Image" src="https://github.com/user-attachments/assets/71d65dfb-0016-4cff-a871-691af035018a" />
@codemile set env
"SHELLOPTS": "braceexpand"
in claude code settings.json this can't work?
@caozhiyuan it was already enabled and didn't work.
@codemile in env ?
Nope, setting the env doesn't work.
<img width="541" height="272" alt="Image" src="https://github.com/user-attachments/assets/a25482be-a7f4-41d9-a665-a38460fc204c" />
@codemile
check you settings.json
<img width="1021" height="491" alt="Image" src="https://github.com/user-attachments/assets/22b05250-bff2-4db6-901e-33abbf68dd72" />
@codemile In claude cli.js, you can see the setting of the SHELLOPTS attribute, but gitbash is incompatible, causing the issue. As long as SHELLOPTS is not empty, the new attribute will not be applied, so there won't be any problem.
<img width="820" height="167" alt="Image" src="https://github.com/user-attachments/assets/ae70954d-90d4-4d5d-bd0f-c5dccfb1cb0c" />
There are too many bugs in the latest version, especially when using Claude Code in Windows environments. It is recommended to use version 2.1.7 @codemile
@codemile I have implemented the second workaround: https://github.com/anthropics/claude-code/issues/18748#issuecomment-3762646331
My build, run and test procedures are all in bash scripts and Claude code was not able to execute them. After applying the workaround now it works fine. I think that one more thing had to be applied - use 'slource script.sh' instead ./script.sh to call inside Claude. And there was a minor fix I can't recall now that has to be applied to the scripts related to BASHSOURCE. I will provide more details when I'm on my desktop
@orlinkata that fix gives you the wrong impression that you've fixed the problem, but it only works for a few commands that it fixes. I had hooks that were failing because of (no content), and other stuff. It's not worth risking it, because you need to baby sit the Claude runs to see if it's getting (no content).
What makes this bug so deadly is that Claude assumes (no content) means success. That will get you at some point.
@caozhiyuan nope, none of those env settings work. I've been using WSL for awhile now and it seems stable.
@codemile I added the environment variables above to my settings.json file without any problems, as you can see in the screenshot above. Could you please share a screenshot of your settings.json file?
Same issue for az commands
@caozhiyuan it's weird how it's working for you. I'm jealous.
<img width="676" height="344" alt="Image" src="https://github.com/user-attachments/assets/a0692376-9836-4cae-8ad2-aaf585bf6023" />
I had to use this and added az commands folder to the scan dirs. And had to change __to_unix_path() a little bit cause it was causing some errors
@caozhiyuan I downgraded Claude to 2.1.7 but still have the problem. I thought that version was supposed to work. I feel like this bug is gaslighting me, because I thought this worked a few weeks ago and was a new bug introduced after 2.1.7.
<img width="958" height="624" alt="Image" src="https://github.com/user-attachments/assets/4e3e3476-65c8-4088-a004-b55fbc6d6148" />
@codemile your claude code use git bash or ? default shell is git bash. and should output
$ echo $SHELLOPTS
braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
@codemile echo $CLAUDE_CODE_GIT_BASH_PATH
install latest git windows https://gitforwindows.org/ @codemile
@caozhiyuan thanks for your help! It's working now with 2.1.7
To get it to work, I had to uninstall Claude from the System Settings and then delete my
~/.claudefolder, then I reinstalled Claude and switched to the stable channel. Seems it was a cache related issue, but deleting~/.claude/plugins/cachewasn't enough. So I did a full reinstall which worked.I did a test with 2.1.20 but it's still broken but switching back to 2.1.7 works.
<img width="535" height="398" alt="Image" src="https://github.com/user-attachments/assets/084a221c-6ce4-44b1-90e9-c0cdc47ea1a0" />
It seems this bug was introduced in 2.1.9
When reviewing the 2.1.9 release note, the only thing related to the terminal is this change, which doesn't sound related but unless they made undocumented changes, then I guess it must be the source of the bug
<img width="927" height="701" alt="Image" src="https://github.com/user-attachments/assets/895f8566-1ac2-420c-9fca-b7aa9a2fd20e" />
Summary
When Node.js
spawn()executes a shell script file via bash on Windows MINGW64 (Git Bash), stdout is not captured. The file descriptors are not properly inherited when bash forks a subprocess to execute the script.Evidence
| Command Pattern | stdout Captured? |
|----------------|-----------------|
|
spawn("bash", ["-c", "echo hello"])| ✅ Yes ||
spawn("bash", ["-c", "node -e '...'"])| ✅ Yes ||
spawn("bash", ["-c", ". script.sh"])| ✅ Yes (sourced) ||
spawn("bash", ["-c", "source script.sh"])| ✅ Yes (sourced) ||
spawn("bash", ["-c", "script.sh"])| ❌ No ||
spawn("bash", ["script.sh"])| ❌ No ||
spawn("bash", ["-c", "(script.sh)"])| ❌ No (subshell) ||
spawn("python", ["script.py"])| ✅ Yes |Key Finding
The issue occurs only when bash forks a subprocess to execute a shell script file. When the script is sourced (runs in the same shell process), stdout is captured correctly.
Root Cause
This is a file descriptor inheritance bug in Git Bash (MINGW64) when used with Node.js:
spawn("bash", ...)and sets up pipes for stdio-c "echo hello"), it executes in the current process and stdout flows correctlyWhy npm/pnpm Are Affected
On Git Bash,
npmresolves to a shell script wrapper at/c/Users/.../npmwhich internally runsnode npm-cli.js. When Claude Code runs:Bash finds and executes the npm shell script file, triggering the fd inheritance bug.
Why npm.cmd Works
npm.cmdis a Windows batch file, not a shell script. When run via:Bash delegates to Windows to run the .cmd file, which doesn't trigger the bash fork behavior.
Workarounds
1. Source scripts instead of executing them
2. Use .cmd versions on Windows
3. Inline the command instead of using script files
Proposed Fix
In the command resolution layer, detect MINGW64 and resolve shell script wrappers to their
.cmdequivalents:Trivial Workaround
This is obviously very suboptimal, but works well in practice.
I've uninstalled it and switched to opencode. Downgrading claude code to 2.1.7, npm --version still doesn't work, although it worked before. Who knows what this software is doing in the background.
The shellwrapperfix causes issues sometimes. Some commands wont run correctly you will get issues like: c/Program could not be found
probably some escaping that is happening incorrectly
The vs code extension appears to work when the CLI does not.
2.1.31, works well
<img width="582" height="559" alt="Image" src="https://github.com/user-attachments/assets/e4648570-b982-4ae6-8518-6f779d128f5d" />
Update: Git for Windows upgrade + MSYS2 Stdout Bridge workaround (2026-02-18)
Finding 1: Git for Windows upgrade significantly improves the situation
Upgrading Git for Windows from 2.34.1 (2021) to 2.53.0 resolved stdout capture for Windows native .exe processes:
| Component | Before | After |
|-----------|--------|-------|
| Git | 2.34.1 | 2.53.0 |
| Bash | 4.4.23 | 5.2.37 |
| MSYS2 runtime | 3.1.7 (2021-10) | 3.6.6 (2026-01) |
After upgrade, commands like
git,docker,npm,node,python,poetry,gcloud,firebase,gh,az, etc. all produce captured stdout correctly. However, bash builtins (echo,pwd,printf) and MSYS2 utilities (ls,cat,grep,head,uname, etc.) still lose stdout.Finding 2: Root cause clarification
The issue is specifically that Claude Code's pipe captures stdout from Windows native .exe processes but not from MSYS2 binaries or bash builtins, which use a different I/O layer (msys-2.0.dll).
Evidence:
echo hello > /dev/null→ exit 0 (shell works, just stdout pipe is broken)echo hello > /tmp/file.txt→ file created with content (file I/O works)cmd.exe /c echo hello→ "hello" captured correctly (Windows native I/O works)echo hello→ exit 1, no output (MSYS2 I/O → Claude Code pipe broken)Finding 3: MSYS2 Stdout Bridge workaround
We developed a workaround that wraps MSYS2 commands to redirect their output through
cmd.exe /c type(Windows native), making it visible to Claude Code:Set via
BASH_ENVin Claude Code'ssettings.json:Result
| Category | Before | After workaround |
|----------|--------|-----------------|
| Windows .exe (git, docker, npm, etc.) | Works (after Git upgrade) | Works |
| Bash builtins (echo, pwd, printf) | Broken | Works |
| MSYS2 utilities (ls, cat, grep, etc.) | Broken | Works |
| Pipes & chains (
echo x \| grep x) | Broken | Works |Full workaround script: https://gist.github.com/ (will publish separately)
Recommendation
Is this issue fixed or is the issue still there? I had the issue still on Claude Code v2.1.47. updated to Git for Windows 2.53.0 . Had to revert back to using the mingw64-workarounds.sh fix
It would be really great if this could be fixed. It is wasting so much time (and tokens!) and making the entire development process a pain. Yes ... there are work arounds ... but c'mon ... just fix the bug, please?
Comment for <https://github.com/anthropics/claude-code/issues/18748>
---
We've been investigating this extensively on Windows 10 with Git for Windows 2.53.0.
What works: Downgrading to Claude Code 2.1.40 fully resolves the issue — CLI tool stdout is captured correctly without any workarounds. No BASH_ENV tricks needed.
What we tried that did NOT help:
claude.execorrupts the parent process stdout pipe, so even output flushed beforemain()disappearsCREATE_NO_WINDOWon subprocess Popen calls — causes ~150s pipe hang on Git 2.53.0.batfile wrapper — cmd.exe output not capturedWe're now pinned to 2.1.40 with
DISABLE_AUTOUPDATER=1.A fix is available as a Claude Code plugin: powershell-default
Install:
Adds a native Pwsh tool (shows as
Pwsh(...)in the UI). Commands use PowerShell syntax directly. When enabled, Bash tool is blocked. Works on any OS with PowerShell 7+.PR: https://github.com/anthropics/claude-code/pull/35761
Why? Doesn’t it cause Claude to need to write commands differently?
Did I do something wrong?
[https://gist.github.com/Sid180603/276580f6c006e2ad2b92a76f04c2f415](url)
### Fix: WSL Bash stdout capture — native Windows wrapper (confirmed working)
Root cause
Claude Code's Bash tool spawns bash.exe and captures stdout via a Node.js pipe. On Windows with WSL2, bash.exe is the WSL launcher — commands execute inside the Linux VM, and stdout must cross the hypervisor boundary back to the Windows host process. This pipe silently drops data. Commands execute correctly, but Claude Code receives nothing → (Bash completed with no output).
This is not a buffering issue. LD_PRELOAD + stdbuf (forcing line-buffered stdout) does not fix it. The pipe itself is broken at the VM boundary.
Solution
A ~200-line C# Windows-native .exe that replaces bash.exe as CLAUDE_CODE_GIT_BASH_PATH. It bypasses the broken pipe entirely using file-based I/O:
Claude Code (Node.js) → bash.exe (wrapper) → wsl.exe → bash (writes to temp file on Windows FS)
↓
wrapper reads temp file with File.ReadAllText()
↓
Console.Write() → Node.js captures it ✓
How it works:
(does NOT capture wsl.exe stdout — that's the broken pipe)
Important gotcha: -c -l flag handling
Claude Code sends: bash -c -l "shopt -u extglob ... && eval '...' ...".
The -l (login shell) flag comes after -c. A naive wrapper that takes args[1] as the command will get -l instead of the actual command string. The wrapper must skip flags after -c and pass them through to the bash invocation.
Source code
Full source (C#, .NET Framework 4.x — ships with Windows): WslBashWrapper.cs
<https://gist.github.com/Sid180603/276580f6c006e2ad2b92a76f04c2f415>
Setup
mkdir "$env:USERPROFILE\.claude\bin" -Force
~/.claude/shell-fixes/WslBashWrapper.cs
& "C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe"
/nologo /optimize
/out:"$env:USERPROFILE\.claude\bin\bash.exe" `
"$env:USERPROFILE\.claude\shell-fixes\WslBashWrapper.cs"
{
"env": {
"CLAUDE_CODE_GIT_BASH_PATH": "C:\\Users\\<username>\\.claude\\bin\\bash.exe"
}
}
Default is "Debian". Override with env var:
"CC_WSL_DISTRO": "Ubuntu-24.04"
What was tried and didn't work
Not a buffering issue — the pipe itself drops data
cmd.exe output still goes through the broken pipe
Works, but loses native Docker/WSL tool access
Environment
Confirmed working for: echo, multiline output, docker ps, git status, whoami, pwd, complex piped commands with exit code propagation.
Seems to be a duplicate of https://github.com/anthropics/claude-code/issues/19663.