Claude Codeのheredocが固まる原因 — macOSのHomebrew bashとの相性
macOSでHomebrewのbash 5.1以降がPATHの先頭にあると、500バイト超のheredoc書き込みが固まり、対象ファイルが空になることがあります。原因の切り分けと回避策をまとめます。
macOSでHomebrewのbash(5.1以降)をPATHの先頭に置いていると、Claude CodeのBashツールがcat > file <<'EOF'のheredocで書き込む場面で固まることがあります。目安は本文が約500バイトを超えるときです。Claudeが長めのファイルを書くたびに、120秒待たされたうえ、書き込み先のファイルが0バイトになります。
これはGitHubのissue #92262で報告されている症状です。issueはopenのままで、修正は入っていません。この記事では、報告の内容、自分の環境が当てはまるかの確認、今すぐ取れる回避策を順に説明します。
症状は「固まる」と「ファイルが空になる」の2つ
issueによると、書き込む内容が513バイトから65535バイトの範囲にあるとき、Bashツールの呼び出しがタイムアウトまで止まります。出力は1バイトも返りません。
厄介なのは2つ目の症状です。シェルはリダイレクトの時点で、書き込み先を先に切り詰めます。heredocの本文が届く前にコマンドが止まるので、既存のファイルが空のまま残ります。報告者は約2KBのmix.exsでこれに遭い、ファイルが空になって、heredocより後ろのコマンドも実行されなかったと書いています。
issueが示す再現条件は次のとおりです。
issueが挙げる発症条件
OSと既定のbash
macOSで、bash 5.1以降がPATHの先頭にある環境です。
brew install bashの結果としてよくある状態です。本文のサイズ
heredocの本文が513〜65535バイトのときだけ固まります。500バイト前後までと、64KB以上は通ります。
本文の中身
影響するのは総バイト数だけです。行数や内容は関係せず、heredocを使わない約1.9KBの1行コマンドは問題なく動いたと報告されています。
issueの環境は、Claude Code 2.1.260、macOS 26.5.2(arm64)、/opt/homebrew/bin/bashのGNU bash 5.3.15です。bash 5.3.9でも別のissueで同じ報告があるとされています。/bin/bash(3.2.57)では起きないと書かれています。
なぜ500バイトを境に固まるのか
issueの説明では、原因はClaude Codeではなく上流のbashにあります。
bash 5.1はheredocの実装を一時ファイル方式からパイプ方式に変えました。一時ファイルに戻るのは65536バイト以上のときだけです。これはLinuxの既定のパイプ容量に合わせた値です。
一方、issueはmacOSのPIPE_BUFが512バイトであることを根拠に挙げています(報告者はgetconf PIPE_BUF /が512を返すことを確認しています)。bashは本文をパイプに書き込み、満杯になるとそこでブロックします。ところが本文を読み出す子プロセスは、まだ生成されていません。誰も読まないパイプを待ち続けるデッドロックです。
bashを経由しないスクリプトファイルの直接実行でも、同じ結果が出たそうです。
| bash | 400B | 600B〜20KB | 100KB |
|---|---|---|---|
| Homebrew 5.3.15 | 400B完了 | 600B〜20KB固まる | 100KB完了 |
/bin/bash 3.2.57 | 400B完了 | 600B〜20KB完了 | 100KB完了 |
境目が両側にあるのは、小さい本文はパイプに収まり、大きい本文は一時ファイル経由になるためです。固まるのは中間の帯だけです。
Claude Code側で症状が悪化する3つの条件
issueは、デッドロックをデータ損失にまで広げているClaude Code側の挙動を3つ挙げています。
- BashツールがPATHで見つかったbashを選び、Homebrew版を使ってしまう
- 書き込み先が先に切り詰められるため、失敗した書き込みが既存ファイルを壊す
- auto modeとbypass-permissions modeが、ファイル書き込みにWriteツールよりheredocを勧める(auto modeの挙動も参照)
3つ目は、issue本文によると、モデルへのこの種の指示が別のissue(#92178など)で指摘されているという話です。つまり、macOSでHomebrewのbashを入れている人ほど、ファイルを大きく書く依頼でこの罠に当たりやすくなります。
自分の環境が当てはまるか確かめる
まず、シェルから見えるbashとそのバージョンを調べます。
which -a bash
bash --version | head -1
getconf PIPE_BUF /which -a bashの先頭が/opt/homebrew/bin/bash(Intel Macなら/usr/local/bin/bash)で、バージョンが5.1以上なら、PATHの先頭は影響を受ける側です。PIPE_BUFが512なら、issueと同じ前提です。PATHの並びはインストール方法でも変わります。ネイティブ版とHomebrew版の違いはClaude CodeのMacインストール手順で扱っています。
ただし、Claude Codeが実際にそのbashを使うとは限りません。公式のenv-vars解説では、Bashツールのシェルは次の順で決まります。
CLAUDE_CODE_SHELLが動作するbashかzshを指していれば、それを使う- 未設定なら、
$SHELLがbashかzshのとき、それを使う - それ以外は、PATH上や標準の場所にある最初の動作するzsh、次にbashを選ぶ
Bashツールが使うシェルが想定外の環境でどうなるかは、Claude CodeでNushellは使えるかが参考になります。
macOSの既定のログインシェルはzshなので、この解説どおりなら$SHELLのzshが選ばれ、問題は出ないはずです。issueは$SHELLの値に触れていません。ログインシェルをHomebrewのbashに変えている場合や、CLAUDE_CODE_SHELLで指定している場合は、確実に当たる側です。
推測で決めず、実際に選ばれたシェルを確かめるには、Claudeに次のコマンドをBashツールで実行させます。
Bashツールで echo "$BASH_VERSION" と ps -p $$ -o comm= を実行して、出力をそのまま見せて$BASH_VERSIONが空ならzsh上で動いています。5.3.15(1)-releaseのような値なら、影響を受ける側のbashです。
回避策 — シェルの固定・本文サイズ・Writeツール
どれもissueが挙げた方法か、公式の環境変数の仕様から導ける方法です。
1. CLAUDE_CODE_SHELLで/bin/bashを指定する
CLAUDE_CODE_SHELLは、Bashツールのコマンドを実行するシェルを指定する環境変数です。bashかzshのパスを受け付け、動作しないパスは無視されて自動検出に戻ります。settings.jsonのenvに書けば、起動のたびに設定する必要はありません。
{
"env": {
"CLAUDE_CODE_SHELL": "/bin/bash"
}
}この方法は、issueが提案する「/bin/bashを明示して呼ぶ」を、利用者の側で実現するものです。注意点が1つあります。公式の説明では、Claude Codeは起動時に~/.bashrcなどを読み込んでエイリアスやシェル関数を取り込みます。bashを3.2に切り替えると、~/.bashrcに5.x専用の記法があれば、そこで警告が出るかもしれません。切り替えたら、よく使うコマンドが動くかを一通り試してください。
2. 本文を512バイト以下に収める
発症しない帯は、約500バイトまでと64KB以上です。長いファイルを分割して追記する方法でも、各heredocを小さくできます。ただし、書き込むたびにバイト数を気にするのは現実的ではありません。1つ目の回避策が使えないときの次善策です。
3. 書き込みをWriteツールに寄せる
CLAUDE.mdに、ファイルの作成と上書きにはheredocでなくWriteツールを使う、と書いておく方法があります。次のような短い規約で十分です。
## ファイル書き込み
- 新規作成と全体の上書きには、Bashのheredocではなく Write ツールを使う
- Bashで書くのは、1行で済む追記やリダイレクトだけにするauto modeではモデルがheredocを選びやすいと報告されているため、明文化しておく意味があります。ただし、モデルが規約を常に守る保証はありません。シェルの固定と併用すれば、規約が守られなかった場合も止まらずに済みます。
issueは、shopt -s compat44でbash 5.1以前の一時ファイル方式に戻せると書いています。報告者の環境では解決を確認済みです。一方で、compatレベルの文書化されていない副作用に頼る方法でもあります。Claude Codeがシェルを起動する時点で設定できる場面は限られるため、ここでは紹介にとどめます。
タイムアウトを延ばしても解決しない
固まるのが120秒だと分かると、BASH_DEFAULT_TIMEOUT_MSを短くするか長くしたくなります。公式の仕様では、フォアグラウンドのBashコマンドのタイムアウトは既定で120000ミリ秒です。
ただし、デッドロックは時間では解けません。変わるのは待たされる長さだけで、ファイルが空になる点は同じです。タイムアウトの仕組みはBASH_DEFAULT_TIMEOUT_MSの解説にまとめています。
空になったファイルの復旧
切り詰められた内容はシェルのゴミ箱に移るわけではないため、復旧の頼りになるのは、gitなどのバージョン管理か、エディタ、Time Machineのバックアップです。
事前に備えておくこと
- 上書きを任せる前に、変更をコミットまたはスタッシュしておく
- 未追跡のファイルを含む作業では、Claudeに書かせる前にコピーを取る
- 症状が出たら、まず
git statusで空になったファイルを確認し、git restoreで戻す
まとめ
macOSでHomebrewのbashが選ばれていると、約500バイトを超えるheredocがパイプ容量の壁にぶつかります。症状は120秒の停止と、書き込み先が空になることです。原因は上流のbashで、Claude Code側にも修正の提案が上がっています。
実際に自分の環境で起きるかは、echo "$BASH_VERSION"で選ばれたシェルを確かめるのが近道です。当たっていたら、CLAUDE_CODE_SHELLで/bin/bashを指定するのが、issueの提案にも沿う回避策です。併せて、上書きの前にコミットしておけば、被害は戻せる範囲に収まります。