Windows: Bash tool commands are cut at about 8,191 characters, including a per-session environment prefix that grows with every SessionStart; double backslashes are halved before the shell sees the command
Status Open
Reported on v2.1.295
Maintainer reply None cached
Workaround ✓ Mentioned in description ↑
Activity 2 comments · opened Oct 9, 2026
Environment
- Claude Code 2.1.295, the desktop app (Code tab), Windows 11 Pro 10.0.26300.
- The Bash tool's shell:
/usr/bin/bash5.3.15(2)-release from Git for Windows (MINGW64_NT-10.0-26300 3.6.10). - A plugin with a SessionStart hook that writes to
$CLAUDE_ENV_FILE:openai-codex/codex1.0.6, whosescripts/session-lifecycle-hook.mjsdoesfs.appendFileSync(process.env.CLAUDE_ENV_FILE, "export NAME=value\n")for three variables on every SessionStart.
Symptoms
- A Bash tool command above a few kilobytes fails before anything runs:
``''
/usr/bin/bash: -c: line 46: unexpected EOF while looking for matching
```
The size at which it fails shrinks over the life of a session. Early in the day a 3.9 KB command ran and a 7 KB one failed; by the afternoon a 3,838-byte command failed while a 3,000-byte one ran.
- A double backslash in a command reaches the shell as a single backslash.
printf '%s\n' 'A\\B'printsA\B; a quoted heredoc containing the lineC\\DprintsC\D. Python scripts, regular expressions, and Windows paths written through the Bash tool are silently altered (we lost characters to\\bbecoming a backspace and\\2026becoming a byte before we understood it).
Mechanism, as observed
The tool runs every command as one bash -c argument. From tr '\0' '\n' < /proc/$$/cmdline inside a command:
/usr/bin/bash
-c
source /c/Users/<user>/.claude/shell-snapshots/snapshot-bash-<id>.sh 2>/dev/null || true && export TEMP='D:\AI\CTEMP' TMP='D:\AI\CTEMP' && export CODEX_COMPANION_SESSION_ID='<session id>'
export CODEX_COMPANION_TRANSCRIPT_PATH='C:\Users\<user>\.claude\projects\<project>\<session id>.jsonl'
export CLAUDE_PLUGIN_DATA='C:/Users/<user>/.claude/plugins/data/codex-openai-codex'
export CODEX_COMPANION_SESSION_ID='<session id>'
... (the same three lines, 16 times in all) ...
: && shopt -u extglob 2>/dev/null || true && { \builtin unalias -- 'unsetenv'; \builtin unset -f -- 'unsetenv'; } >/dev/null 2>&1 || true && eval '<the command>' && pwd -P >| /d/AI/CTEMP/claude-<id>-cwd
- The contents of
CLAUDE_ENV_FILEare prepended verbatim to every command. The hook appends to that file on every SessionStart, and SessionStart fires on startup, resume, clear, and compaction, so the file accumulates one copy per event. After one long day the prefix held 16 copies and measured 4,538 bytes beforeeval. (The shell snapshot, 3,957 bytes, is sourced separately and contains none of these lines.) - The whole
-cstring is cut at about 8,191 characters, the Windowscmd.execommand-line limit: with that prefix, a 3,000-byte command ran (prefix + wrapper + command ≈ 7,600) and a 3,838-byte command failed (≈ 8,440). The cut leaves theeval 'quote unclosed, which is the "unexpected EOF while looking for matching''" message; the reported line number is the line of theeval`, the last line of the prefix. Nothing of the command runs. - The backslash halving happens before bash: for a command containing
Q1\\Z, the shell's own/proc/$$/cmdlinealready containsQ1\Z. So the collapse is in the tool's argument construction on Windows, not in bash.
Reproduction
- Windows, the desktop app or CLI with Git Bash as the Bash tool's shell, and any SessionStart hook that appends an
exportline to$CLAUDE_ENV_FILE(the Codex plugin does). - After a few resumes or compactions, in the Bash tool:
tr '\0' '\n' < /proc/$$/cmdline | grep -c 'export CODEX_COMPANION_SESSION_ID'prints the number of copies (16 here) andtr '\0' '\n' < /proc/$$/cmdline | wc -cthe total. - A quoted heredoc of fifty 100-character lines piped to
wc -cfails with the EOF message; thirty lines run. printf '%s\n' 'A\\B'printsA\B.
Expected
- The environment file should hold one copy of each export: either the hook's additions replace earlier ones with the same name, or the tool deduplicates lines, or the file is reset before each SessionStart run.
- On Windows the command should reach bash through stdin or a temporary script rather than a
-cargument subject to the 8,191-character limit, or the tool should refuse an over-long command with a clear message instead of cutting it. - Backslashes should arrive in the shell exactly as written.
Workarounds in use
- Scripts go to a file through the Write tool and run as
python -I file.pyorpwsh -File file.ps1; Bash commands stay under about 3 KB, and the budget shrinks by about 270 bytes per SessionStart with this plugin installed. - Nothing with a double backslash is typed into the Bash tool directly.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗