[BUG] Windows sandbox breaks Gradle/JVM loopback IPC ("Unable to establish loopback connection") — still repros on 2.1.209 (#44857 closed without fix)
What's wrong
Any Gradle task (or any JVM tool that opens an NIO Selector) run from Claude Code's sandboxed Bash on Windows fails at JVM startup with Unable to establish loopback connection. ./gradlew --version works (it opens no Selector), but ./gradlew testDebugUnitTest / assembleDebug fail. The identical command in a normal terminal (outside the agent sandbox) works fine.
This is the same bug as #44857 (closed without a fix) and related to #18545 and #28018. Filing fresh because #44857 is closed and it still reproduces on the current release (2.1.209).
What should happen
Gradle/JVM/MCP tools should run inside the sandboxed Bash on Windows — the agent should be able to build/test Android & JVM projects, as it can in a normal terminal.
Error Messages/Logs
> ./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)
at java.base/sun.nio.ch.WEPollSelectorProvider.openSelector(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.UnixDomainSockets.connect(Unknown Source)
at java.base/sun.nio.ch.SocketChannelImpl.connect(Unknown Source)
at java.base/sun.nio.ch.PipeImpl$Initializer$LoopbackConnector.run(Unknown Source)
Steps to Reproduce
- On Windows, in any Android/Gradle project (JDK 17+, e.g. the Android Studio JBR), from Claude Code's Bash tool run:
./gradlew testDebugUnitTest. - It fails at JVM startup with the loopback
IOExceptionabove. ./gradlew --versionsucceeds (no NIOSelectoris opened, so no AF_UNIX socket).- The same command in a normal PowerShell/Git Bash terminal →
BUILD SUCCESSFUL.
Root cause (credit to the analysis in #44857): JDK 17+ on Windows uses WEPollSelectorImpl, which opens an AF_UNIX socket for its wakeup pipe. The socket path derives from %TEMP%, and Windows caps sockaddr_un.sun_path at 108 bytes. Inside the sandbox the effective path exceeds 108 → EINVAL. This happens even when %TEMP% looks short — mine was C:\Users\<user>\AppData\Local\Temp (~38 chars) and it still failed, so the sandbox's path handling pushes the effective socket path over the limit.
Workaround that works (confirms it's a hard blocker, not user error)
settings.json:"sandbox": { "filesystem": { "allowWrite": ["C:\jtmp"] } }mkdir C:\jtmp- Force a very short TEMP for the child process:
export TEMP='C:\jtmp' TMP='C:\jtmp' && ./gradlew testDebugUnitTest→ BUILD SUCCESSFUL (28/28 unit tests ran).
Note: setting TMP/TEMP via settings.json env does not work (overridden), and ~/.bashrc is not sourced by the Bash tool — the only thing that worked was forcing TEMP inline on the command plus the sandbox allowWrite above.
Environment
- Claude Code version: 2.1.209 (Windows desktop)
- OS: Windows 11
- Terminal/Shell: Claude Code's sandboxed Bash tool (MSYS/Git Bash)
- JDK: 21 (Android Studio JBR), Gradle 8.5
- Regression: Yes — reported as a regression in #44857 (which was on 1.2.234); still broken on 2.1.209. Exact last-working version unknown (already broken at 1.2.234).
Suggested fix
Provide child processes a short, sandbox-managed TEMP/TMP (or avoid leaking the long/virtualized path into %TEMP%), so JVM WEPollSelectorImpl can create its AF_UNIX wakeup socket within the 108-byte sun_path limit. This unblocks Gradle, plain java, and MCP servers on Windows.
Showing cached comments. Read the full discussion on GitHub ↗
3 Comments
Was hitting this but managed to have it self diagnose an easy workaround without any config changes, just a bit it added to its memory:
Adding data points from our own investigation into what looks like the same issue (Selector.open() / AF_UNIX EINVAL — "Invalid argument" on connect, Windows).
What's consistently correlated with failure
Across every test run so far (multiple machines), the failure occurs if and only if both of these hold:
TEMP/TMP is left at its OS default value (%USERPROFILE%\AppData\Local\Temp) — not manually overridden, and
the Windows username is 9+ characters.
Any manual override of TEMP/TMP — short or long, inside or outside the user profile — avoids the failure in every test we've run. This points at something specific to how the default AppData\Local\Temp value resolves/expands at that username length (e.g. an internal short-path generation step, a junction/reparse point under AppData\Local, or a fixed-format assumption in the socket-path construction that only triggers off the literal default env-var expansion) — not a generic path-length or location rule. We haven't pinned down the exact mechanism yet.
Additional data point: AF_UNIX loopback failures are not limited to the %TEMP%-path-length theory.
While setting up React Native/Android Studio on Windows 11 today, I hit this exact bug family, with a twist the current theories don't explain.
Repro: from Claude Code's PowerShell tool, launched Android Studio directly with Start-Process. It crashed about 40s after showing its splash screen with the same "Unable to establish loopback connection" / AF_UNIX EINVAL signature as this issue.
Applied the workaround from this issue (short TEMP=C:\jtmp). Confirmed it fixes a minimal repro (plain Selector.open() in a standalone java program) - went from failing to succeeding.
However, Android Studio still crashed identically even with the short TEMP in place. This time the failure was in IntelliJ Platform's own DirectoryLock (its single-instance lock/activation mechanism), which binds an AF_UNIX socket at a fixed, short path directly under %LOCALAPPDATA%\Google\ProductName\.port (about 64 chars total, well under the 108-byte sun_path limit). Same bind()/connect() EINVAL signature.
Definitive confirmation: had the user launch the identical studio64.exe themselves directly (Start Menu, not through Claude Code) - it opened and ran perfectly, completed the SDK setup wizard, downloaded system images, and the created AVD emulator (a native, non-JVM process) also launched fine when started by the user. Everything that failed was launched via Claude Code's sandboxed Bash/PowerShell tool; everything the user launched directly worked.
Why this matters: the %TEMP%-path-length theory doesn't explain the DirectoryLock case - a fixed ~64-char path outside of %TEMP% still fails identically. This suggests the sandbox is breaking AF_UNIX bind()/connect() more broadly for child processes it spawns on Windows (e.g. a job object / restricted token / WFP filter scoped to the sandbox's process tree), not only when the socket path is derived from an over-long %TEMP%. A path-length-only fix will likely be insufficient for tools (Android Studio, Gradle daemon, Kotlin compiler daemon, etc.) that bind/connect AF_UNIX sockets at fixed, already-short locations outside %TEMP%.
Suggested repro for maintainers: on Windows, from Claude Code's Bash/PowerShell tool, run Start-Process on studio64.exe vs. having the user launch the same binary themselves (Start Menu / double-click), and compare outcomes. If this reproduces, it confirms the sandbox itself (not just %TEMP% length) is the blocker for any JVM app that opens AF_UNIX sockets, including ones unrelated to %TEMP%.
Reported via a Claude Code session, at the user's request, so other developers hitting this (and future Claude Code sessions) don't have to rediscover it from scratch.