Claude Code Windowsで「unexpected EOF」——8KB超のheredocが壊れ、バックスラッシュが消える原因

Claude Code Windowsで「unexpected EOF」——8KB超のheredocが壊れ、バックスラッシュが消える原因

Windows版Claude CodeでBashコマンドが8KBを超えるとunexpected EOFで失敗し、バックスラッシュも消える不具合を整理。原因はMSYS2 bashのargv経由8192バイト制限。再現データと今すぐできる回避策をまとめた。

エンジニアのゆとです。

Windows版のClaude Codeで、大きめのファイルをheredoc(cat > file <<'EOF' ... EOF)で書き込もうとすると/usr/bin/bash: -c: line 102: unexpected EOF while looking for matching \”`のようなエラーで止まる、という報告をGitHubのIssueで見つけた。しかも厄介なことに、このエラーが出ずに「成功」した場合でも、書き込んだ内容のバックスラッシュがこっそり半分に減っていることがあるらしい。

自分はふだんmacOS+WSLの組み合わせで動かしてるので、Git Bash特有のこの不具合を自分の環境で踏んだことはない。ただ、報告の中身が異常に細かくて——bisectionで閾値を測定し、31件の失敗コマンドをbash -nの構文チェックにかけ直し、バイト単位で入出力を比較する、というところまでやってある。ここまで再現条件が詰められた一次情報はそうないので、Windowsで開発してる人向けに、症状と原因、そして今すぐできる回避策を整理しておく。

github.com
[BUG] Windows: Bash commands >8KB silently truncated, backslash pairs silently collapsed · Issue #93915 2026年9月12日に報告された本件のIssue本体。bisectionによる閾値測定・バイト比較・再現スクリプトつき。

まず結論 — 何が起きていて、原因は何か

症状発生条件原因
unexpected EOF while looking for matching でheredoc書き込みが失敗するコマンドが約8,000バイトを超えたときMSYS2 bashのargv再構築処理が固定8,192バイトのスタックバッファで打ち切られる
バックスラッシュのペア(\\)が半分に減るサイズに関係なく、常に同じargv再構築処理がWindows流のエスケープ規則を適用してしまう(推測、未確定)
どちらもClaude Code側にエラー表示が出ない、または見当違いの行を指す上記どちらの症状でも壊れているのは転送経路であって、コマンド自体の構文は正しいため

どちらの症状も、Windows版Claude CodeがBashツールのコマンドをGit Bashのbash.exe -cに「引数(argv)」として渡していることが根本原因。同じ内容をbashの標準入力(bash -s)経由で渡すと、259KBまでバイト単位で一致することが確認されている。つまりbash自体にもWindowsにも問題はなく、両者をつなぐMSYS2ランタイムの実装が原因、というのがこのIssueの整理だ。

症状1 — 8KB超のheredocが「見当違いの行」でエラーになる

エラーメッセージは毎回こういう形になる。

/usr/bin/bash: -c: line 102: unexpected EOF while looking for matching `''

紛らわしいのは、指摘される行番号が実際に構文の壊れている場所ではなく、たまたまアポストロフィを含む行(won'tやHDD's、$(date '+%Y-%m-%d')など)を指す点。報告者はこの31件の失敗コマンドを実際のセッション記録から抽出して、構文チェック専用のbash -nに再度通している。結果は31件中30件が「構文エラーなし」——つまりコマンド自体は正しく、単に転送の途中で切り落とされていただけだったことになる。

もう一つの根拠は、エラーが指す行番号がコマンドサイズに関わらず一定だったこと。8KBから24KBまでサイズを変えても、常に同じ行番号でエラーが出た。内容によって位置が変わる構文エラーならこうはならない。固定位置での打ち切り、という見立てと整合する。

実測されたコンポーネント別の上限は次のとおり。

コンポーネント上限
Git for Windowsのbash.exe -c8,203文字
同じMSYS2ランタイム上のecho.exe20,000文字は無傷で通過
ネイティブバイナリのpython.exe32,000文字は無傷で通過
WindowsのCreateProcess自体32,767文字(コマンドライン全体、ドキュメント値)

Windows自体はコマンドライン全体で32,767文字まで許容しているし、同じMSYS2ランタイム上の別バイナリでも20,000文字は通る。詰まっているのはbash -cだけ、という切り分けになっている。

実際の閾値が8,203文字よりずっと低く見える理由

Claude CodeのBashツールは、ユーザーが書いたコマンドをそのまま渡しているわけではない。実際にはこういう形でラップされている。

bash -c "source <shell snapshot> && export TEMP=... && shopt -u extglob
         && { unalias ...; } && eval '<ユーザーのコマンド>' && pwd -P >| <cwdファイル>"

このeval '...'ラッパーのせいで、ユーザーコマンド中のシングルクォート1文字が、エスケープ後は'"'"'という5文字に膨張する。アポストロフィを含む自然な文章(won'tや$(date '+%F')など)は、見た目のサイズより実際の消費バイト数がずっと大きい。加えてラッパー自体が約1KBを消費する。だから体感としては「8,000バイトを超えたあたりから失敗し始める」という報告になる。

症状2 — バックスラッシュが黙って消える(サイズ無関係)

こちらのほうが厄介だと報告者も書いている。エラーは一切出ないまま、ペアになったバックスラッシュだけが静かに半分になる。

  • \\ → \ になる
  • \\\\ → \\ になる
  • Pythonの正規表現r'\\d+'がr'\d+'として書き込まれ、「バックスラッシュにマッチする」が「数字にマッチする」に変わってしまう
  • C:\Users\nameのような孤立したバックスラッシュは壊れずに残る

検証は295バイトという小さいペイロードでも再現している。つまりチャンクに分けて小さく送っても回避にならない、サイズに依存しないバグだということ。

ペイロードサイズ-c経由(現状)標準入力経由
295バイト壊れる(-4バイト)一致
1KB壊れる(-16バイト)一致
6.5KB壊れる(-100バイト)一致
15KB書き込み失敗一致
52KBWinError 206一致
259KBWinError 206259KBまでバイト単位で一致

原因を掘り下げる — MSYS2のglob()が抱える固定8,192バイトのスタックバッファ

症状1の原因は、報告者いわく「ドキュメント化済み」とのこと。MSYS2ランタイムがWindowsのコマンドラインからargvを再構築する際、build_argv()(dcrt0.cc)がglob()を呼び出し、このglob()がUnicode変換のためにスタック上に確保した固定8,192バイトのバッファを使っているという、上流の既知issueが引用されている。

github.com
msys2-runtime Issue #178 glob()がスタックバッファでUnicode変換を行っており、8,192バイトを超える引数を扱えないという上流の既知issue。

このglob()が呼ばれる条件は「コマンドライン中に?*[\"'(){}のいずれかの文字が含まれる場合」で、シングルクォートもダブルクォートも対象に入っている。つまりeval '...'ラッパーを使う通常のシェルコマンドは、ほぼ確実にこの経路を通る。

症状2(バックスラッシュの消失)については、報告者自身も「原因は推測であり未確認」と明言している。同じargv再構築処理がWindows流のエスケープ規則(バックスラッシュ+クォートをエスケープとして解釈する)を適用しているのではないか、というのが仮説。ただし測定結果自体(295バイトでも起きる、-s経由なら起きない)は再現性のある事実として提示されている。

参考として、OpenAIのCodex CLIでも同種の「Windowsで大きいペイロードをargv経由で渡すと壊れる」バグが報告されているとのこと。

github.com
openai/codex Issue #15003 別のAIコーディングツールでも同種のargv経由サイズ制限バグが報告されている、独立した参考情報。

何が効かないか — 手を出す前に知っておく

報告者が実際に試して効かなかった方法もまとめられている。同じ沼にはまらないための情報として先に共有しておく。

MSYS=noglob環境変数: -cの上限を8,203文字から32,731文字まで引き上げる効果は確かにある。ただしglobbing処理自体を無効化する副作用で、クォートを含むコマンドのargv再構築が別の形で壊れ、eval: line 1: unexpected EOF while looking for matchingという、結局同じ系統のエラーで死ぬ。常にeval '...'ラッパーを通るClaude Codeの実装とは相性が悪く、サイズ上限と引き換えに確実な破損を得るだけになる。

ペイロードをチャンクに分割する: 症状1(打ち切り)には有効でも、症状2(バックスラッシュ消失)は295バイトでも起きるため、分割しても意味がない。

Base64エンコードする: エンコード後のペイロードも結局argvを通るうえ、サイズが約33%増える分だけ状況が悪化する。

今すぐできる回避策

Claude Code本体側の修正を待たずに、手元でできる対処をまとめておく。

1. 大きいファイルはheredocでなくWriteツールに書かせる

Bashツールの-c経由の制約を受けるのはheredocやシェル一行コマンドの話で、Claude CodeのWrite/Editツールを使ったファイル書き込みはこの経路を通らない。8KBを超えるような設定ファイルやコード生成を頼むときは、「catでheredoc書き込みして」ではなく「Writeツールでファイルを作成して」と明示的に指示したほうが、この不具合を丸ごと避けられる。

2. どうしてもBash経由で大きい内容を書く場合は、複数回に分けて追記する

1回あたりのeval後サイズが数千バイトに収まるよう、>>での追記を複数回に分ける。ただしこれは症状1(打ち切り)の緩和にしかならず、バックスラッシュを含む内容(正規表現・Windowsパスの二重エスケープなど)には症状2が別途乗ってくる点は変わらない。

3. バックスラッシュを含む内容はBash経由で書かせない

正規表現パターンやエスケープが絡む文字列は、サイズに関係なく壊れる可能性がある。Pythonスクリプトやコード中の\\d+のような表現をClaude Codeに生成・書き込みさせる場合は、Bashのheredoc経由ではなくWrite/Editツール経由にしておくのが無難。

4. WSL2に切り替える

そもそもMSYS2/Git Bashを経由しない構成にすれば、このglob()のスタックバッファ制約自体を踏まなくなる。WSL2側には別種の重さ・権限プロンプトの課題があるので、そちらも合わせて把握したうえで選んだほうがいい。

Claude CodeがWSLで重い・権限プロンプトが消えない——症状別に公式ドキュメントとGitHub Issueで原因を特定した
Claude CodeがWSLで重い・権限プロンプトが消えない——症状別に公式ドキュメントとGitHub Issueで原因を特定したClaude CodeをWSL2で使うと応答開始まで1〜6分かかる、起動直後にフリーズする、settings.jsonの許可設定が効かない——症状別にGitHub Issueと公式ドキュメントの一次情報で原因を切り分け、対処法とネイティブWindows/WSLの選び方をまとめた。読む →

似ているが原因が違う「unexpected EOF」もある

同じエラーメッセージで検索すると、今回の話とは無関係の原因がヒットすることがあるので、切り分けの参考に載せておく。報告者自身がIssue内で「関連するが原因は別」として整理していた。

  • #26742(および重複の#26946、#27163): Windowsのユーザー名にアポストロフィが含まれるケース(C:\Users\SeanO'Loughlinなど)。パス中のクォート文字が原因で、ペイロードサイズやargvとは無関係。#27163の報告者はmacOSでも同じエラーを再現しており、パス説を明示的に否定している
  • #48317、#29619: Markdownをheredocやghコマンドに渡す際のクォート処理に起因するもので、いずれも活動停止で自動クローズ済み

今回の#93915が他と違うのは、「ペイロードがbash -nの構文チェックを通る」「エラーの行番号がサイズに関わらず一定」という2点を実測で示している点。単に「heredocでエラーが出た」という報告よりも切り分けの精度が高い。

Git Bash自体の検出・パス設定でつまずいている場合は、これとは別の話になる。

Claude CodeでGit Bashが検出されない——CLAUDE_CODE_GIT_BASH_PATHを設定しても直らない原因を公式ドキュメントで潰す
Claude CodeでGit Bashが検出されない——CLAUDE_CODE_GIT_BASH_PATHを設定しても直らない原因を公式ドキュメントで潰すWindowsでClaude Codeが「requires git-bash」を出し続ける原因を、ファイル名の落とし穴・v2.1.219の挙動差・EDRによるブロックに分けて解説。設定例と診断コマンドつき。読む →

現状のステータス(2026年9月13日時点)

このIssueは2026年9月12日に報告されたばかりで、bug・has repro・platform:windows・area:bashのラベルが付いているものの、Anthropic側からのコメントはまだ付いていない。直近のバージョン(v2.1.269、9月11日リリース)の変更点にも該当する修正は含まれていなかった。

報告者が提案している修正案は「コマンドをargvではなく標準入力(bash -s)経由、または一時ファイル経由で渡す」というシンプルなもので、これならglob()のスタックバッファを含むargv再構築処理自体を通らなくなる。実装コストの見立てとしても大きな話には見えないので、直る可能性は低くないと自分は見ているけど、現時点ではあくまで未修正の不具合として扱ったほうがいい。

Windows環境で日常的に大きいファイルをBash経由で生成している人は、claude --versionで自分のバージョンを確認しつつ、CHANGELOGを定期的にチェックしておくのがいい。

code.claude.com
Claude Code Changelog - Claude Code Docs バージョンごとの修正点一覧。今回の不具合が直った場合もここに反映される。

まとめ

Windows版Claude CodeでBashツールを使っていて、8KBを超えるheredoc書き込みがunexpected EOFで失敗する、あるいはコード中のバックスラッシュがこっそり消えるという症状に心当たりがあるなら、原因はコマンド内容の書き方ではなく、Git Bash(MSYS2)がargvを再構築する処理の実装上の制約にある可能性が高い。大きいファイルや正規表現・エスケープを含む内容は、Bashのheredoc経由ではなくWriteツール経由で書かせるのが、今のところ一番確実な回避策になる。

FAQ

macOSやWSL2でも同じ不具合は起きる?

報告されているのはGit for Windows(MSYS2ランタイム)特有のbash -c実装に起因する不具合。macOSのbashやWSL2内のLinuxネイティブbashはこの経路を通らないため、同じ症状にはならない。WSL2に切り替えれば根本的に回避できるが、WSL2固有の起動の重さや権限プロンプトの課題は別途ある。

8KBぎりぎりのコマンドなら大丈夫?

サイズだけを見て安全圏を判断するのは危険。eval '...'ラッパーの影響でシングルクォート1文字が5文字分に膨張するため、見た目のコマンド長より実際の消費バイト数はかなり大きくなる。さらにバックスラッシュの消失はサイズに関係なく発生するため、「短いから安全」とは言えない。

MSYS=noglobを設定すれば直る?

-cの文字数上限自体は8,203文字から32,731文字まで上がるが、globbing処理を無効化する副作用でクォートを含むコマンドのargv再構築が別の形で壊れる。Claude Codeは常にeval '...'ラッパーを使うため、この設定は逆効果になりやすい。報告者も「テスト済みで却下」としている。

この不具合はClaude Code固有の問題?

厳密には違う。bash自体(OS非依存)にもWindows自体(32,767文字まで許容)にも問題はなく、両者をつなぐMSYS2ランタイムのglob()実装が原因。ただし、この制約が実際に踏まれる状況を作っているのはClaude Codeが任意サイズのユーザーペイロードをargv経由で渡している設計であるため、「Claude Codeが原因で顕在化する」とは言える。

← 記事一覧に戻る