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
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/bash 5.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/codex 1.0.6, whose scripts/session-lifecycle-hook.mjs does fs.appendFileSync(process.env.CLAUDE_ENV_FILE, "export NAME=value\n") for three variables on every SessionStart.

Symptoms

  1. 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.

  1. A double backslash in a command reaches the shell as a single backslash. printf '%s\n' 'A\\B' prints A\B; a quoted heredoc containing the line C\\D prints C\D. Python scripts, regular expressions, and Windows paths written through the Bash tool are silently altered (we lost characters to \\b becoming a backspace and \\2026 becoming 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_FILE are 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 before eval. (The shell snapshot, 3,957 bytes, is sourced separately and contains none of these lines.)
  • The whole -c string is cut at about 8,191 characters, the Windows cmd.exe command-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 the eval ' quote unclosed, which is the "unexpected EOF while looking for matching ''" message; the reported line number is the line of the eval`, 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/$$/cmdline already contains Q1\Z. So the collapse is in the tool's argument construction on Windows, not in bash.

Reproduction

  1. Windows, the desktop app or CLI with Git Bash as the Bash tool's shell, and any SessionStart hook that appends an export line to $CLAUDE_ENV_FILE (the Codex plugin does).
  2. 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) and tr '\0' '\n' < /proc/$$/cmdline | wc -c the total.
  3. A quoted heredoc of fifty 100-character lines piped to wc -c fails with the EOF message; thirty lines run.
  4. printf '%s\n' 'A\\B' prints A\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 -c argument 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.py or pwsh -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.

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗