Windows の Claude Code で Bash の出力が消える問題を解決した話

Windows で Claude code を使っていると「bash の出力が得られない」とか言われてとても不便していた。 Fable5 に調査を依頼して解決できたのでその顛末を以下に示す。

TL;DR

  • Windows + Scoop 環境の Claude Code で、Bash ツールが常に (Bash completed with no output) を返す問題に遭遇した
  • 原因は Claude Code の bash 自動検出が git-bash.exe(別窓のターミナルを開くランチャー)を誤選択していたこと。コマンドの出力はパイプではなく別窓の mintty に流れて消えていた
  • 環境変数 CLAUDE_CODE_GIT_BASH_PATH に正しい bash.exe のパスを設定して解決した
[System.Environment]::SetEnvironmentVariable(
    "CLAUDE_CODE_GIT_BASH_PATH",
    "C:\Users\<ユーザー名>\scoop\apps\git\current\bin\bash.exe",
    "User")

環境

  • Windows 11 Pro
  • Claude Code 2.1.218(当時最新)
  • Git for Windows 2.33.1(Scoop でインストール、C:\Program Files\Git は存在しない)

発端: Backlog の認証エラー…に見えた

Backlog のリポジトリを clone しようとして「認証エラーだ」と Claude Code に相談したのが始まり。ところが調査を始めると、そもそも Bash ツールのすべてのコマンドが (Bash completed with no output) を返す状態だった。echo hello すら出力が返ってこない。

迷走フェーズ

最初は的外れな仮説が続いた。

  • 仮説 1: Backlog の認証方式の問題 → Bash が動かないので検証すらできない
  • 仮説 2: 作業ディレクトリが存在しない → 存在していた
  • 仮説 3: Git がインストールされていない → Scoop でインストール済みだった
  • 仮説 4: WSL の bash.exe と競合しているMSYSTEM=MINGW64 が確認でき、Git Bash 自体は起動していた

このあたりで「AI が推測で答えている」ことを指摘され、実証ベースの調査に切り替えた。

転機: bash は動いている、stdout だけが消えている

ファイルリダイレクトを使ったテストで大きな手がかりが得られた。

echo hello > /tmp/test.txt   # → ファイルには正しく書き込まれる
echo hello                   # → 出力が返ってこない

つまり bash は起動しており、コマンドも実行されている。stdout がツールに返る経路だけが壊れている

さらに奇妙なことに、この bash 環境では lscatwhich も見つからなかった。PATH が Windows 形式(セミコロン区切り)のまま MSYS 側で変換されておらず、/usr/bin が通っていなかった。

切り分け: Node.js から同じ bash を起動してみる

「bash が悪いのか、Claude Code が悪いのか」を切り分けるため、Node.js の spawnSync で同じ bash.exe を起動するテストを書いた。

const { spawnSync } = require('child_process');
const r = spawnSync(
  'C:\\Users\\<ユーザー名>\\scoop\\apps\\git\\current\\usr\\bin\\bash.exe',
  ['-c', 'echo hello'],
  { encoding: 'utf8' });
// → status=0, stdout="hello\n" 正常に取得できる

Node.js からは stdout が正常に取れた。 bash は無罪。問題は Claude Code 側のプロセス起動にある。

バイナリ解析: 正式なオーバーライド変数を発見

Claude Code は Bun でコンパイルされた単一バイナリだが、JavaScript ソースが文字列として埋め込まれているため、grep -a(バイナリ強制テキスト検索)と dd で内部実装を読める。

grep -aob "CLAUDE_CODE_GIT_BASH_PATH" ~/.local/share/claude/versions/2.1.218
dd if=<バイナリ> bs=1 skip=<オフセット> count=3000 | tr -c '[:print:]' '\n'

抽出したコードから bash 検出ロジックが判明した。

// Windows での bash 探索順序
// 1. 環境変数 CLAUDE_CODE_GIT_BASH_PATH(最優先・存在しないパスなら起動拒否)
// 2. C:\Program Files\Git\bin\bash.exe
// 3. C:\Program Files (x86)\Git\bin\bash.exe
// 4. PATH 上の git から相対パスで解決

ポイントは 2 つ。

  • シェル指定の正式な環境変数は CLAUDE_CODE_SHELL ではなく CLAUDE_CODE_GIT_BASH_PATHCLAUDE_CODE_SHELL は設定しても無視される)
  • Scoop 環境では 2〜3 の標準パスが存在しないため、4 のフォールバック解決に入る

決定打: プロセスツリー

PowerShell の Get-CimInstance Win32_Process で親子関係を調べたところ、決定的な証拠が出た。

claude.exe(15384) ← pwsh.exe ← WindowsTerminal.exe   … 自分のターミナル
  └─ git-bash.exe(4324)                              … Claude Code が起動した「シェル」
       └─ mintty.exe(23228)                          … 別窓のターミナル GUI!

Claude Code は bash として git-bash.exe を起動していたgit-bash.exe は「新しいターミナルウィンドウ (mintty) を開くためのランチャー」であり、標準入出力をパイプに返さない。

  • コマンドは mintty 内の bash で実行される → 実行自体は成功する
  • 出力は別窓に流れる → Claude Code には何も返らない
  • exit code だけはランチャー経由で伝播する

「実行されるのに出力だけが空」という症状が完全に説明できた。GitHub の Issue #38882(可視のコンソールウィンドウが出現する)とも整合する。

修正

正しい bash(ランチャーではなく本体のラッパー)を明示指定する。

[System.Environment]::SetEnvironmentVariable(
    "CLAUDE_CODE_GIT_BASH_PATH",
    "C:\Users\<ユーザー名>\scoop\apps\git\current\bin\bash.exe",
    "User")

Claude Code を再起動すると:

echo hello-stdout-test && git --version && pwd
# hello-stdout-test
# git version 2.33.1.windows.1
# /c/dev/job/naritasan/src

stdout が返ってきた。 PATH 変換も正常化し、lsstat などの GNU ツールも使えるようになった。

オチ: 認証エラーは存在しなかった

Bash 復旧後に本題の clone を実行したら、認証エラーは一切発生せずに成功した。Git Credential Manager に Backlog の認証情報が保存済みだったためだ。

当初の「認証エラー」の正体は、Bash が壊れていて git コマンドの結果がまともに見えていなかっただけの可能性が高い。最初の症状報告が別の問題の影だった、というデバッグあるあるだった。

教訓

  • 「認証エラー」という報告を鵜呑みにしない。 実行基盤 (この場合は Bash ツール) が壊れていると、あらゆるエラーが別の顔で現れる
  • 推測より切り分け。 ファイルリダイレクトで「bash は動いている」を、Node.js の spawn テストで「bash は無罪」を、それぞれ 1 実験で確定できた
  • コンパイル済みバイナリでも文字列は読める。 Bun/Node の単一バイナリは grep -a + dd で内部実装をかなり読める。ドキュメントにない環境変数 CLAUDE_CODE_GIT_BASH_PATH はバイナリ解析でしか見つからなかった
  • プロセスツリーは嘘をつかない。 Win32_Process の親子関係が最終的な決定打だった
  • 標準外のパス (Scoop、winget のユーザーインストール等) に Git を入れている Windows 環境では、同じ問題を踏む可能性が高い。同症状の人は CLAUDE_CODE_GIT_BASH_PATH を試してほしい

参考リンク