[BUG] Trailing stdout dropped due to stdout/stderr sentinel race in the Bash tool (Windows / Git Bash)
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?
When a command run through the Bash tool writes to stderr and then exits very quickly, a trailing chunk of its stdout is silently dropped from the captured result. No error is surfaced — the model just receives output that is shorter than what the command actually produced.
It is a timing race, not "any stderr drops output":
ls/grepthat error on stderr → trailing output is kept ✅gitfatal (writes stderr, then exits fast) → trailing output is dropped ❌ (reproducible 3/3)- Adding
2>&1→ trailing output is restored ✅
This is a silent data-integrity defect: because git output drives merge/commit/status decisions, dropped tails can make the tool act on incomplete data without any warning.
What Should Happen?
The captured output should always contain the command's complete stdout — the trailing sentinel should appear in every case, exactly as it does for ls/grep/2>&1. Output capture should be race-free: both streams fully drained before the command is treated as finished, so trailing stdout can never be dropped.
Error Messages/Logs
There is **no error message** — that is part of the problem (the truncation is silent). Below is the observed captured output.
Case D (bug) — trailing `E1` is missing:
S1
<no git output, no E1 line — capture ends here>
Case E (same command + `2>&1`) — trailing `E1b` present as expected:
S1b
fatal: not a git repository (or any of the parent directories): .git
E1b
Compare with cases that behave correctly (trailing sentinel present):
# A
S3a
mid
E3
# B
S4
ls: cannot access '/no/such/path': No such file or directory
E4
Steps to Reproduce
From a directory that is NOT a git repository, run each line as a single Bash tool call:
# A — pure stdout (control)
echo S3a; echo mid; echo E3 # -> E3 present ✅
# B — ls error on stderr (control)
echo S4; ls /no/such/path; echo E4 # -> E4 present ✅
# C — grep error on stderr (control)
echo S2; grep x /no/such/dir; echo E2 # -> E2 present ✅
# D — git fatal on stderr, then trailing echo <<< THE BUG
echo S1; git log --oneline -3; echo E1 # -> E1 MISSING ❌ (3/3 runs)
# E — same as D, stderr merged into stdout
echo S1b; git log --oneline -3 2>&1; echo E1b # -> E1b present ✅
Observed: only case D drops its trailing sentinel, deterministically.
Claude Model
None
Is this a regression?
Yes, this worked in a previous version
Last Working Version
_No response_
Claude Code Version
2.1.217 (Claude Code)
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
PowerShell
Additional Information
Environment
- OS: Windows 11
- Shell used by Bash tool: Git Bash (MINGW64), the Windows default
- Claude Code version: 2.1.217
Root-cause hypothesis
The Bash tool appears to append a completion marker to each command (e.g. <cmd>; echo <MARKER>) and reads captured output until that marker is seen. If stdout and stderr are captured as two separate streams, a process that writes a burst to stderr and exits immediately (like git fatal) can let the reader observe the marker and stop draining before the last stdout chunk is flushed — truncating the tail. 2>&1 collapses everything onto one ordered stream, so trailing stdout is guaranteed to precede the marker. This also matches why running the same command in a normal terminal always looks correct (human reads raw output, no marker mechanism).
Related observations (need separate investigation; listed so they don't dilute the deterministic bug above)
- Cross-command buffer bleed: the same race can leave un-drained stdout in the pipe buffer, which then gets prepended to the next command's result — real output from an earlier command appearing, misplaced, in a later one. (Distinguishable because the "extra" text can be found verbatim in an earlier command's real output in the same session.) Restarting the session clears it.
- Downstream model confabulation: when output is corrupted/truncated this way, the model sometimes fails to recognize it as damaged and "fills in" a plausible complete success report (a merge that didn't happen, a commit hash that never existed). This is a model-reliability concern amplified by the tool bug — fewer corrupted inputs would mean fewer such events.
- Read/Glob encoding (separate fault line): the
Read/Globtools appear to garble files containing box-drawing characters / large CJK blocks / emoji, and sometimes falsely reportFile does not exist(suspected UTF-8-vs-codepage / path-encoding issue on Windows). Needs its own repro.
Current workarounds (local mitigation only)
- Append
2>&1to any command that may write to stderr. - Restart the session/shell as soon as output looks anomalous.
- Prefer a trusted external terminal for bare
git/ fast-exiting commands.