[BUG] Windows/Git-Bash: harness bash -c wrapper wedges mid-session — every Bash tool command fails with "-c: line 111: unexpected EOF matching" (often silently); direct bash works
Summary
On Windows + Git Bash, the Bash tool's command wrapper wedges mid-session: from some point on, every Bash tool invocation — even a trivial pwd — fails with
/usr/bin/bash: -c: line 111: unexpected EOF while looking for matching `"'
(exit 2), and produces zero side effects (a printf foo > file.txt never creates the file). Often it surfaces as the even more confusing silent "command completed with no output" with no error at all.
The key fact: **bash itself is fine. The harness's bash -c wrapper is broken. A trivial command like pwd should be bash -c "pwd" (1 line). The error says -c: line 111 — proving the harness prepends ~110 lines of plumbing/preamble around my 1-line command, and that preamble currently contains an unterminated "**, so bash parses to EOF and never reaches my command.
This makes the Bash tool 100% unusable for the rest of the session, frequently silently (no error text), which is far worse than a loud failure — the agent thinks commands "succeeded with no output" and proceeds on false assumptions.
Impact / severity
- Total, persistent loss of the Bash tool for the remainder of a session once it trips.
- Silent in the common case (
completed with no output, no error) → the agent cannot tell thatgit commit, test runs, etc. did nothing. This is a correctness/safety hazard, not just an annoyance. - No in-tool recovery:
dangerouslyDisableSandbox: truedoes not help (it isn't a sandbox issue), and there is no "reset shell" tool. The only escape is to abandon Bash entirely (switch to the PowerShell tool) or restart the whole session.
Environment
- Claude Code 2.1.177
- Windows 11 Pro, build 10.0.26200.0
- Git for Windows 2.53.0.windows.2, GNU bash 5.2.37(1)-release (x86_64-pc-msys)
- Shell: Git Bash (the Bash tool); PowerShell 7 tool also available and unaffected
Reproduction evidence (decisive contrast)
All four observations are from the same machine, same session, same moment:
1. Bash tool — broken (live):
# tool call: printf liveprobe > zz_live_probe.txt; echo BASHTOOL_RAN
/usr/bin/bash: -c: line 111: unexpected EOF while looking for matching `"' (exit 2)
# zz_live_probe.txt is NOT created — zero side effects
Earlier in the same wedged state the identical class of command returned "completed with no output" with no error line at all (silent).
2. Interactive ! bash mode — same failure (shares the same wedged shell):
! pwd
/usr/bin/bash: -c: line 111: unexpected EOF while looking for matching `"'
3. Direct bash from the PowerShell tool — WORKS (proves bash + env are fine):
& "C:\Program Files\Git\bin\bash.exe" -c "echo DIRECT_BASH_OK; pwd"
# DIRECT_BASH_OK
# /c/dev/sugar-dating (exit 0)
Works with BASH_ENV both set (/etc/bash.bashrc) and explicitly cleared.
4. PowerShell tool — fully functional (this entire diagnosis was performed with it).
Root-cause analysis (what is and isn't the cause)
Ruled out by direct inspection:
- bash binary — fine (observation 3).
/etc/bash.bashrc(BASH_ENVtarget) — stock MSYS, 60 lines, balanced quotes, unmodified for months. ClearingBASH_ENVchanges nothing.- The shell snapshot (
~/.claude/shell-snapshots/snapshot-bash-*.sh) — 22 lines, clean, all quotes balanced (thergfunction +export PATH='...'are correct). Not the source of "line 111". - User/project hooks — none of the
PreToolUse: Bashhooks rewrite the command (noupdatedInput/hookSpecificOutput.commandanywhere). They only allow/deny/add-context.
What remains — and is therefore the culprit — is Claude Code's own Bash-tool command-assembly layer: the code that builds the final bash -c "<snapshot-source + cwd-restore + plumbing + USER COMMAND>". Two corroborating facts:
-c: line 111for a 1-line user command ⇒ ~110 lines of harness-injected preamble around it.- The unterminated
"is in that injected preamble (the user command here,printf ... ; echo ..., has perfectly balanced quotes).
Onset is mid-session, not from boot. Bash worked for dozens of commands, then broke — immediately after launching two concurrent run_in_background Bash commands containing quotes + redirection (e.g. codex review --base master -c sandbox_mode="danger-full-access" > /c/.../out.txt 2>&1). The most consistent explanation: the harness drives a persistent bash session, and a backgrounded command with quotes/redirection left the persistent shell's input buffer with an open "; every subsequent command is then appended after that unclosed quote, so bash accumulates lines and only errors at EOF (line 111). The wedge lives in the in-memory persistent shell, which is why every on-disk artifact (bashrc, snapshot) is clean yet the tool is broken, and why a session restart (fresh shell) is the only full reset.
Why this must NOT be closed as a duplicate / "works on my machine"
This is a distinct, reproducible failure fingerprint with a full root-cause writeup, not a vague "no output" report. Please do not fold it into a stale closed issue without addressing the actual mechanism. For the record, it is related to but not covered by:
- #26481 (
set -o onecmd injection, Windows/MINGW no output) — closed; this report adds the-c: line 111: unexpected EOF matching "signature + the mid-session-wedge-from-backgrounded-quoted-command trigger. - #57125 (hook
commandwrapped inbash -c '...', apostrophes brick the session) — closed; here it is the harness's own command wrapper (not a hook command) and it wedges mid-session via the persistent shell, not at a fixed substituted path. - #52983 (shell snapshot creation fails) — different: our snapshot is created and clean; the corruption is in the assembly around it.
- #21915 / #54037 / #19525 / #26545 (Windows Bash tool "no output" / exit 1) — same family, but those were closed as fixed; this is a 2.1.177 recurrence with a precise
line 111reproduction and a clear "persistent-shell got an open quote" hypothesis.
If you believe it duplicates one of the above, please first explain how that issue's fix is supposed to prevent a persistent Git-Bash session from being left with an unterminated " by a backgrounded quoted command, and why a clean snapshot + clean bashrc + working direct-bash still yields -c: line 111: unexpected EOF through the tool. A drive-by "dup, closing" is not an answer to that.
Concrete asks
- Detect-and-respawn: when a
bash -cexits with a shell parse error (unexpected EOF,unexpected end of file) that does not originate from the user command, treat the persistent shell as corrupt and respawn a fresh one automatically (and retry once), instead of poisoning every subsequent command. - Never fail silently: a non-zero
bash -cmust always surface stderr to the tool result. The "completed with no output" path (empty result on a parse failure) is the most dangerous part — it makes the agent believe destructive/important commands ran. - Quote/heredoc isolation for backgrounded commands: ensure a
run_in_backgroundcommand with quotes/redirection cannot leave the shared persistent shell's input buffer in an open-quote state (run backgrounded commands in an isolated subshell / separate process so they can't wedge the foreground session). - Debuggability: on a wrapper parse failure, emit (at least under a debug flag) the actual assembled
bash -cstring so the offending injected line is visible. Right now the user cannot see the 110-line preamble that breaks. - A
reset shell/ fresh-shell escape hatch exposed to the tool, so recovery doesn't require restarting the entire session.
Workaround (for others hitting this)
- The PowerShell tool is unaffected — switch all shell work to it. On Windows it's the more reliable shell anyway.
- To restore the Bash tool specifically, restart the Claude Code session (regenerates a clean persistent shell). Deleting on-disk snapshots does not help — the corrupt state is in memory.
- Diagnostic to tell "didn't run" from "ran but stdout swallowed":
printf marker > probe.txtvia the Bash tool, then readprobe.txtwith a non-Bash tool. If the file is absent, the command body never executed (wrapper parse failure); if present, it's an stdout-capture problem instead.