Bash tool: heredoc file writes deadlock and truncate the target file on macOS with Homebrew bash
Affected environment
- Claude Code 2.1.260 (Claude Desktop, Code tab)
- macOS 26.5.2, arm64
/opt/homebrew/bin/bash— GNU bash 5.3.15(1)-release ← affected (5.3.9 also reported, in #33768)/bin/bash— GNU bash 3.2.57(1)-release ← not affected- Actual pipe capacity measured at 512 bytes;
getconf PIPE_BUF /→ 512
Trigger condition: macOS with a bash 5.1 or newer first in PATH, which is the default outcome of brew install bash.
Summary
Asking Claude Code to write a file whose contents are between 513 and 65535 bytes, in any mode that steers file writes to Bash heredocs, causes the call to hang for the full 120s timeout and leaves the target file truncated to 0 bytes.
The deadlock itself is an upstream bash bug (detailed below), but two Claude Code behaviours turn it into data loss:
- The Bash tool uses PATH-resolved bash, so on a machine with Homebrew bash it selects the affected interpreter instead of the unaffected
/bin/bash. - The redirect truncates the target before the heredoc body is known to have arrived, so an existing file is destroyed by a command that then never completes. This is the part that causes real damage — a 120s stall is annoying, silently emptying a file the user asked to have written is not recoverable.
This is made considerably more likely by auto mode and bypass-permissions mode, which explicitly instruct the model to prefer cat <<EOF over the Write tool (see #92178, #88041, #90599, #88791). On any macOS machine with Homebrew bash, that guidance routes routine file writes straight into this failure.
I hit this writing a ~2 KB mix.exs — the file was emptied, nothing after the heredoc in the command ran, and the call produced zero bytes of output before timing out.
Root cause (upstream bash, not Claude Code)
bash 5.1 changed here-documents from temp files to pipes, falling back to a temp file only at ≥ 65536 bytes — Linux's default pipe capacity, which bash assumes. macOS pipe capacity is 512 bytes. Bash writes the body into the pipe and blocks once it fills, but has not yet forked the child that would drain it → permanent deadlock.
Because the fallback threshold is 64 KB, the broken range is bounded on both sides: bodies ≤512 bytes fit the pipe, bodies ≥64 KB take the temp-file path, and only the middle deadlocks.
Confirmed with no Claude Code involved — a plain script file executed directly:
/opt/homebrew/bin/bash 5.3.15: 400 B OK · 600 B HUNG · 1 KB HUNG · 4 KB HUNG
5 KB HUNG · 20 KB HUNG · 100 KB OK
/bin/bash 3.2.57: all of the above OK
Binary-searching the upper edge on bash 5.3.15 puts it at 65536 exactly.
Bisected lower edge, through the Bash tool:
| Heredoc body | Result |
|---|---|
| 377 B | OK |
| 496 B | OK |
| ~600 B | HANGS |
| ~960 B / 2 KB / 4 KB / 5 KB | HANGS |
Only total body size matters — line count and content are irrelevant, and a 1932-byte single-line command with no heredoc works fine.
Steps to reproduce
brew install bash; confirmwhich bash→/opt/homebrew/bin/bash(5.1+)- Ask Claude Code, in auto mode, to write any file of more than ~500 bytes.
Or directly, as one Bash tool call:
cat > /tmp/probe.txt <<'XEOF'
0001 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
0002 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
0003 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
0004 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
0005 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
0006 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
0007 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
0008 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
0009 eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
XEOF
echo "COMPLETED"
COMPLETED never prints; /tmp/probe.txt is left at 0 bytes. Point it at a file that already has contents and those contents are gone. Dropping to 7 lines (under 512 bytes) succeeds instantly.
Requested fixes
In rough priority order:
- Don't destroy the target on a failed write. Whatever the interpreter does, a tool call that never delivers its payload should not leave the user's file empty. This is the data-loss half and is independently fixable.
- Invoke
/bin/bashexplicitly for Bash tool execution rather than PATH-resolved bash — macOS system bash is 3.2 and unaffected. (Suggested previously in #44564.) - Fail fast: detect the deadlock rather than burning 120s per occurrence, and surface a diagnostic naming the cause.
- Reconsider the auto-mode guidance that steers writes to heredocs over the Write tool, at least on macOS — it maximises exposure to this.
A cheap mitigation, if the shell is spawned in a way that can set it: shopt -s compat44 restores pre-5.1 temp-file heredocs and resolves this completely. I've verified that end to end on this machine. Note it is an undocumented side effect of that compat level, so it is a workaround rather than something to depend on long-term.
Prior reports
Reported at least three times; nothing open currently tracks it.
- #33768 (plugin hooks; correct root-cause analysis) — auto-closed as a duplicate of #33638, which is a Windows SessionStart hook hang and unrelated. That dedup appears incorrect.
- #44564 (background bash) — closed as COMPLETED, but the behaviour is still present in 2.1.260; its only human comment says it is a duplicate of #33768, so the "completed" state looks like a mis-close rather than a fix.
- #62813 (heredoc inside command substitution) — closed NOT_PLANNED / stale.
All three are locked, and the auto-lock message directs filing a new issue referencing them, which is what this is. Note that #62813 framed this as specific to command substitution and #44564 as specific to background commands; it is neither — a plain top-level cat > file <<'EOF' is affected, which is why the practical impact is much wider than those reports suggest.