Claude Media
Claude Code WindowsのGradleがloopback connectionで落ちる原因と回避

Claude Code WindowsのGradleがloopback connectionで落ちる原因と回避

WindowsのClaude Codeで./gradlew testDebugUnitTestだけがloopback connectionで落ちる原因の見立てと、TEMPやJAVA_TOOL_OPTIONSで通す回避策。

WindowsのClaude Codeで./gradlew testDebugUnitTestを実行するとUnable to establish loopback connectionで落ちる。ふだんのターミナルでは同じコマンドが通る。この症状は、GitHubのissue #77508に報告されています。

報告者は原因をサンドボックスと見ています。ただ、スレッドのコメントを読むと、サンドボックスではなくClaude Desktopの配布形式(MSIX)に由来するという見立てが有力です。この記事では、症状の出方、2つの原因説、試す順番の回避策、直らないケースを順に扱います。

どのコマンドが落ちて、どれが通るのか

issueに載っているログは次の形です。

> ./gradlew testDebugUnitTest
FAILURE: Build failed with an exception.
* What went wrong:
java.io.IOException: Unable to establish loopback connection
    at java.base/sun.nio.ch.WEPollSelectorImpl.<init>(Unknown Source)
Caused by: java.net.SocketException: Invalid argument: connect
    at java.base/sun.nio.ch.UnixDomainSockets.connect0(Native Method)
    at java.base/sun.nio.ch.PipeImpl$Initializer$LoopbackConnector.run(Unknown Source)

注目するのは最後の2段です。JDKはセレクターを開くとき、起こす用のパイプをAF_UNIXソケットで作ります。そのconnectが「Invalid argument」で失敗しています。ネットワークの疎通ではなく、JVMの内部で閉じた処理です。

報告者の環境はWindows 11、Claude Code 2.1.209のデスクトップ版、JDK 21(Android Studio同梱のJBR)、Gradle 8.5です。コマンドごとの結果は次のとおりでした。

コマンド結果理由(報告者の説明)
./gradlew --version結果成功理由(報告者の説明)セレクターを開かない
./gradlew testDebugUnitTest結果失敗理由(報告者の説明)JVM起動時にセレクターを開く
./gradlew assembleDebug結果失敗理由(報告者の説明)同上
通常のPowerShell / Git Bashで同じコマンド結果成功理由(報告者の説明)Claude Codeの外

--versionが通るので、「Gradleが入っていない」「JDKのパスが違う」といった疑いはここで消せます。Gradleに限った話でもなく、セレクターを開くJVMのツールなら同じ形で落ちると報告者は書いています。

原因の見立てが2つある

スレッドには原因の説明が2系統あります。

くらべる

スレッド内の2つの原因説

最初の報告

パス長説

JDK 17以降のWindowsではソケットのパスが%TEMP%から作られます。Windowsはsockaddr_un.sun_pathを108バイトに制限していて、サンドボックス内では実効パスがそれを超えてEINVALになる、という説明です。報告者のTEMPは約38文字で短いのに失敗したため、サンドボックスが実効パスを伸ばしているとみています。

コメントで有力

MSIX仮想化説

Claude DesktopはMSIXパッケージで配布されます。そこから起動した子プロセスにもファイルシステムの仮想化が及び、AF_UNIXのソケットファイルがAppData配下へ付け替えられてconnectが失敗する、という説明です。

パス長説だけでは説明しにくい報告が、コメントに並んでいます。

  • ユーザー名が12文字、TEMPが8.3形式に展開された環境で、Selector.open()だけがアプリのシェルで失敗し、単体のPowerShellでは通った(デスクトップ版2.2553.1)
  • 失敗は、TEMPが既定値のままで、Windowsのユーザー名が9文字以上のときに揃って起きた。TEMPを手で上書きすれば、短くても長くてもプロファイル内外でも失敗しなかった
  • TEMPを短くしても直らず、%LOCALAPPDATA%直下の約64文字の固定パスでもAndroid Studioが同じEINVALで落ちた

3つ目だけは別の経路の話で、後半の「直らないケース」で扱います。

サンドボックスのせいとは限らない

issueのラベルにはarea:sandboxが付いています。一方、Claude Codeのサンドボックスの解説ページには、サンドボックスはmacOS、Linux、WSL2で動き、ネイティブWindowsではコマンドが非サンドボックスで実行されると書かれています。

この食い違いに触れるコメントがあります。Claude Desktop 2.19675.0、Temurin JDK 25.0.2、Windows 11(ビルド26200)の環境で、dangerouslyDisableSandbox: trueを付けてもSelector.open()の結果は同じだった、という報告です。同じ人は次の点も書いています。

  • AppData、AppData\Local、AppData\Roaming、AppData\LocalLowへの書き込みが%LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\…に付け替わる
  • %LOCALAPPDATA%\Tempは付け替えられない
  • JDKは、AF_UNIXのbindが失敗したときだけTCPのループバックに切り替わる。この症状ではbindは成功してconnectだけが失敗するため、切り替わらない

同じ現象は、JDKのバグJDK-8312215("Unable to establish loopback connection" when creating a HttpClient instance)にも2023年から報告されています。Microsoft StoreのAPPXアプリで、ファイルシステム呼び出しをリダイレクトされるフルトラスト型のアプリの下で動くJavaが、同じInvalid argument: connectで落ちる、という内容です。JDK側のチケットのステータスはIn Progressで、修正バージョンは設定されていません。

整理すると、症状の引き金は「Claude Desktop(MSIX)から起動したプロセスのAF_UNIXソケット」で、サンドボックスの設定は主役ではない可能性が高い、というのが読み取れる範囲です。サンドボックスを無効にして確かめた報告が1件あるだけなので、断定はできません。

回避策は2つ、短いディレクトリを作ってJDKに教える

どの報告でも効いた手は、ソケットファイルの置き場をAppData外の短いパスへ逃がすことです。例ではC:\jtmpを使います。

方法1: TEMPとTMPをコマンドに直接付ける

最初の報告者が確認した手順です。Git Bash(Claude CodeのBashツール)で、ディレクトリを作ってから環境変数を付けて実行します。

mkdir -p /c/jtmp
export TEMP='C:\jtmp' TMP='C:\jtmp' && ./gradlew testDebugUnitTest

報告者の環境ではBUILD SUCCESSFULになり、28件の単体テストが走りました。注意点が2つあります。

  • settings.jsonのenvでTEMP、TMPを設定しても効かなかった(上書きされる)
  • ~/.bashrcはBashツールから読み込まれないため、そこに書いても無駄だった

そのため、コマンドの行頭にインラインで付けるのが確実です。なお報告者はこのときsandbox.filesystem.allowWriteにC:\jtmpも足しています。ネイティブWindowsでサンドボックスが動かないというドキュメントと合わないので、足さずに試して通るなら不要です。通らなければ、次のように足してみる選択肢が残ります。

{
  "sandbox": {
    "filesystem": {
      "allowWrite": ["C:\\jtmp"]
    }
  }
}

方法2: JAVA_TOOL_OPTIONSでJDKのソケット置き場を指定する

別のコメントでは、Claude自身がこの回避策を見つけてメモリーに残したという報告がありました。JDKのシステムプロパティjdk.net.unixdomain.tmpdirでソケットの置き場所を指す方法です。

mkdir -p /c/jtmp
JAVA_TOOL_OPTIONS="-Djdk.net.unixdomain.tmpdir=C:\\jtmp" ./gradlew assembleDebug

JAVA_TOOL_OPTIONSは起動するすべてのJVMに渡るため、Gradleのランチャー、デーモン、ワーカーまで同じプロパティが届く、と書かれています。ディレクトリはAppDataやProgram Files以外であれば場所を問わない、とのことです。別の環境(デスクトップ版2.2553.1)では、方法1と方法2のどちらでもgradlew assembleDebugが通ったと確認されています。

どちらを選ぶか

状況向く方法
Gradleだけ通ればよい向く方法方法2(子JVMすべてに届く)
Gradle以外のツールも同じ環境変数で動かしたい向く方法方法1
どちらも効かない向く方法次節の切り分けへ

回避策が効いたか、本当に原因が同じかを確かめる

Gradleを回す前に、Selector.open()だけを試す小さなJavaファイルで原因を確かめられます。JDK 11以降なら、ソースファイルを直接実行できます。

// SelectorCheck.java
public class SelectorCheck {
    public static void main(String[] args) throws Exception {
        java.nio.channels.Selector.open().close();
        System.out.println("OK");
    }
}
java SelectorCheck.java

これは筆者が用意した確認用の例で、報告者の1人が書いた「素のSelector.open()」を、実行しやすい形にしたものです。次の順で結果を見ます。

切り分け

Selector.open()の結果から切り分ける

  1. QClaude Codeから実行した SelectorCheck.java は OK を出すか?
    答えがいいえのとき

    Unable to establish loopback connectionが出るなら、Gradleとは無関係です。方法1か方法2の環境変数を付けて、もう一度実行します。

    答えがはい
  2. Q環境変数を付けた SelectorCheck.java は通るか?
    答えがいいえのとき

    AppData外の別のディレクトリで試します。C:\jtmpが存在し、書き込める場所かも確認します。それでも失敗するなら、スレッドにあるTCPループバックへ切り替える手(後述)か、WSL2の利用を検討します。

    答えがはい
  3. Q同じ環境変数で ./gradlew testDebugUnitTest も通るか?
    答えがいいえのとき

    Gradleのデーモンが以前の環境のまま残っている可能性があります。./gradlew --stopでデーモンを止めてから、もう一度実行します。

    答えがはい

環境変数の付け方が決まった

この付け方を、Claude Codeが毎回使えるように記録します。

デーモンが古い環境変数を引き継ぐ可能性は、筆者の推測です。報告スレッドに書かれた事実ではないので、効かなければ--stopを試す、という位置づけにしてください。

Claudeに毎回付けさせる

方法2を報告したコメントは、Claudeが自分でメモリーに書いて、以後はその前置きを付けて実行するようになった、という経緯でした。CLAUDE.mdに書いておけば、メモリーに頼らずプロジェクトで共有できます。たとえば次のような形です。

## Gradle(Windows)
 
- Claude CodeのBashからgradlewを実行するときは、必ず次の前置きを付ける
  `JAVA_TOOL_OPTIONS="-Djdk.net.unixdomain.tmpdir=C:\\jtmp" ./gradlew <task>`
- `C:\jtmp` が無ければ先に作る
- `Unable to establish loopback connection` が出たら、まず前置きが付いているかを確認する

ログが長くなる問題は別の話です。出力の絞り方はClaude CodeでGradleビルドを回す記事で扱っています。

直らないケースと、別の選択肢

Android Studioを起動させる場合

スレッドには、TEMPを短くしても直らなかった報告があります。Claude CodeのツールからStart-ProcessでAndroid Studioを起動すると、約40秒後に同じUnable to establish loopback connectionで落ちました。IntelliJ Platformの単一インスタンス用ロックが、%LOCALAPPDATA%直下の約64文字の固定パスにAF_UNIXソケットを作るため、TEMPの置き換えが届かなかったという説明です。

同じ報告者は、ユーザー自身がスタートメニューから起動したstudio64.exeは問題なく動き、SDKのセットアップもエミュレーターの起動も通ったと書いています。Android Studioそのものは、Claude Codeに起動させず自分で開いておくのが現実的です。Gradleのビルドとテストはこの記事の回避策で通せます。

JDKのTCPループバックに切り替える手

先ほどのコメントには、bindをわざと失敗させる手も載っています。たとえば-Djdk.net.unixdomain.tmpdir=C:\invalid|pathのような不正なパスを指定すると、JDKがTCPのループバックを使うようになるという内容です。コメント自身が「JDKの未文書の挙動に頼っている」と断っているため、前の2つの方法が効かないときの最後の手段にとどめます。

WSL2でClaude Codeを動かす

サンドボックスの解説ページは、Windowsでサンドボックスを使うにはWSL2の中でClaude Codeを動かすよう案内しています。管理者向けの節でも、Windowsの端末にはWSL2かコンテナーへ移ってもらう方法が挙がっています。この症状がWSL2の中でも出るかは、スレッドに報告がありません。移行は環境全体の変更になるため、まずは環境変数で足りるかを確かめるのが先です。

sandbox.failIfUnavailableを設定している組織

sandbox.failIfUnavailableを管理設定で有効にしていると、サンドボックスが動かないネイティブWindowsではClaude Codeが起動時に終了します。この設定の扱いはsandbox.failIfUnavailableの記事にまとめました。

似て見える別の症状

サンドボックスで通信が通らない問題は、原因がまったく違います。psqlやsshがつながらない場合はサンドボックスから接続できないときの切り分けを参照してください。こちらはネットワークの許可リストの話で、Invalid argument: connectというJVM内部のエラーとは別物です。

まとめ

Unable to establish loopback connectionは、JDKのセレクターがAF_UNIXソケットを作る段階で落ちる症状です。名前に反してネットワークの問題ではありません。報告スレッドの状態はopenのままで、修正版の記載もないため、短いパスをJDKに教える回避策を前提に運用することになります。まずSelector.open()だけを試し、通る環境変数の付け方を決めてCLAUDE.mdに残す。この順で進めると、Gradle以外のJVMツールにも同じ対処を使い回せます。

この記事を共有:XはてブLinkedIn