claude-code - 💡(How to fix) Fix [BUG] Claude Desktop (MSIX) bundled bash leaks TEMP=/tmp into child JVMs, breaking Gradle and any Java Selector.open() on Windows

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

java.io.IOException: Unable to establish loopback connection at java.base/sun.nio.ch.PipeImpl$Initializer.run at java.base/sun.nio.ch.PipeImpl$Initializer.init (PipeImpl.java:96) at java.base/sun.nio.ch.PipeImpl.<init> (PipeImpl.java:186) at java.base/sun.nio.ch.WEPollSelectorImpl.<init> (WEPollSelectorImpl.java:78) at java.base/sun.nio.ch.WEPollSelectorProvider.openSelector (WEPollSelectorProvider.java:33) Caused by: java.net.SocketException: Invalid argument: connect at java.base/sun.nio.ch.UnixDomainSockets.connect0 (Native Method)

Root Cause

The MSIX-packaged Bash sets TEMP=/tmp and TMP=/tmp for the shell process. These propagate to every child process via the standard environment. JDK 16+ uses UnixDomainSockets in sun.nio.ch.PipeImpl for the Selector wakeup pipe on Windows (JEP 380), and the wepoll Selector that drives it became default in JDK 17 (JDK-8266369, see JDK 17 release notes). The AF_UNIX path in PipeImpl was "temporarily" disabled in JDK-8280233 and later re-enabled, so whether a given JDK 17.x build triggers depends on whether its PipeImpl currently uses AF_UNIX or TCP for the wakeup pipe. The socket path is derived from TEMP; /tmp is not a valid Windows path, so connect0 returns EINVAL. Verified on JDK 21 and JDK 25.

The Cygwin manual (Environment Variables) calls out exactly this risk:

TMPDIR, TMP, TEMP are converted to UNIX format … will be used by some Cygwin applications, possibly with unexpected results.

Microsoft staff have confirmed on MS Q&A 5599711 that MSIX AppContainer does not formally support Java NIO selectors / Unix-domain sockets — though that thread is about JVM apps packaged as MSIX, not env-leak to child JVMs.

Fix Action

Fix / Workaround

Important context: Anthropic currently does not ship an MSI installer for Claude Desktop on Windows. The only Windows distribution channel is the MSIX-based installer (the public website serves a ~6.7 MB stub that downloads the MSIX). End users cannot opt out of MSIX, which means the "distribute as MSI" workaround commonly suggested for MSIX/JVM incompatibilities (see MS Q&A 5599711) is not available to us — only Anthropic can fix this.

Per-call workaround

Code Example

java.io.IOException: Unable to establish loopback connection
  at java.base/sun.nio.ch.PipeImpl$Initializer.run
  at java.base/sun.nio.ch.PipeImpl$Initializer.init (PipeImpl.java:96)
  at java.base/sun.nio.ch.PipeImpl.<init> (PipeImpl.java:186)
  at java.base/sun.nio.ch.WEPollSelectorImpl.<init> (WEPollSelectorImpl.java:78)
  at java.base/sun.nio.ch.WEPollSelectorProvider.openSelector (WEPollSelectorProvider.java:33)
Caused by: java.net.SocketException: Invalid argument: connect
  at java.base/sun.nio.ch.UnixDomainSockets.connect0 (Native Method)

---

# 1) Bash env shows Cygwin-style /tmp leaked in?
echo "TEMP=$TEMP TMP=$TMP"   # → TEMP=/tmp TMP=/tmp on affected setups

---

# 2) Claude Desktop is the MSIX build?
Get-Process claude | Select-Object -First 1 -ExpandProperty Path
# → contains WindowsApps\Claude_*MSIX

---

import java.nio.channels.Selector;
   public class SelTest {
       public static void main(String[] a) throws Exception {
           Selector s = Selector.open();
           System.out.println("OK: " + s.getClass().getName());
           s.close();
       }
   }

---

cd /tmp && javac SelTest.java && java SelTest
   # → java.io.IOException: Unable to establish loopback connection

---

cmd //c "set TEMP= && set TMP= && java SelTest"
   # → OK: sun.nio.ch.WEPollSelectorImpl

---

unset TEMP TMP; ./gradlew <task>

---

EstablishBuildEnvironment Configuring env variables: [..., MSYSTEM=, CLAUDECODE=]

---

./gradlew --stop          # or, if hung: taskkill /F /IM java.exe
unset TEMP TMP
./gradlew <task>          # daemon respawns with clean env
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • I am using the latest version of Claude Code

What's Wrong?

When Claude Desktop for Windows is installed via the MSIX package, the bundled Cygwin/Git-Bash shell exposes TEMP=/tmp and TMP=/tmp to subprocesses. Any native-Windows JVM (JDK 16+ when PipeImpl uses AF_UNIX for the Selector wakeup pipe) spawned from that Bash fails at Selector.open() with Unable to establish loopback connection / UnixDomainSockets.connect0 EINVAL.

This makes ./gradlew <task> unusable from the Claude Desktop Bash tool. The Claude Code CLI is not affected — only the Desktop App's bundled shell.

Distinct from #41432: that one is about Cowork's VM altering the env of stdio MCP child processes. This one is about the MSIX-bundled shell itself leaking Cygwin-style /tmp into JVM children, independent of Cowork or MCP.

Important context: Anthropic currently does not ship an MSI installer for Claude Desktop on Windows. The only Windows distribution channel is the MSIX-based installer (the public website serves a ~6.7 MB stub that downloads the MSIX). End users cannot opt out of MSIX, which means the "distribute as MSI" workaround commonly suggested for MSIX/JVM incompatibilities (see MS Q&A 5599711) is not available to us — only Anthropic can fix this.

What Should Happen?

The bundled Bash should not propagate Cygwin-style TEMP=/tmp / TMP=/tmp to native-Windows child processes. Either:

  1. Scrub TEMP / TMP before spawning child processes, OR
  2. Translate them to Windows paths (e.g., the user's %LOCALAPPDATA%\Temp), OR
  3. Honor the env field in ~/.claude/settings.json so users can scrub these themselves (currently has no effect on Bash subprocesses on MSIX installs).

./gradlew build invoked from the Claude Desktop Bash tool should succeed without manual unset TEMP TMP per call.

Error Messages/Logs

java.io.IOException: Unable to establish loopback connection
  at java.base/sun.nio.ch.PipeImpl$Initializer.run
  at java.base/sun.nio.ch.PipeImpl$Initializer.init (PipeImpl.java:96)
  at java.base/sun.nio.ch.PipeImpl.<init> (PipeImpl.java:186)
  at java.base/sun.nio.ch.WEPollSelectorImpl.<init> (WEPollSelectorImpl.java:78)
  at java.base/sun.nio.ch.WEPollSelectorProvider.openSelector (WEPollSelectorProvider.java:33)
Caused by: java.net.SocketException: Invalid argument: connect
  at java.base/sun.nio.ch.UnixDomainSockets.connect0 (Native Method)

Steps to Reproduce

Detection first (so you can confirm you're on the affected setup):

# 1) Bash env shows Cygwin-style /tmp leaked in?
echo "TEMP=$TEMP TMP=$TMP"   # → TEMP=/tmp TMP=/tmp on affected setups
# 2) Claude Desktop is the MSIX build?
Get-Process claude | Select-Object -First 1 -ExpandProperty Path
# → contains WindowsApps\Claude_*  → MSIX

Minimal reproducer (no Gradle needed):

  1. In the Claude Desktop Bash tool, create SelTest.java:

    import java.nio.channels.Selector;
    public class SelTest {
        public static void main(String[] a) throws Exception {
            Selector s = Selector.open();
            System.out.println("OK: " + s.getClass().getName());
            s.close();
        }
    }
  2. Compile and run:

    cd /tmp && javac SelTest.java && java SelTest
    # → java.io.IOException: Unable to establish loopback connection
  3. Run again with TEMP / TMP scrubbed:

    cmd //c "set TEMP= && set TMP= && java SelTest"
    # → OK: sun.nio.ch.WEPollSelectorImpl

Same JVM, same JAR, only difference is TEMP / TMP reaching the JVM.

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Last Working Version

No response

Claude Code Version

Claude 1.8555.2 (a476c3) 2026-05-22T23:04:37.000Z

Platform

Other

Operating System

Windows

Terminal/Shell

Other

Additional Information

Root cause

The MSIX-packaged Bash sets TEMP=/tmp and TMP=/tmp for the shell process. These propagate to every child process via the standard environment. JDK 16+ uses UnixDomainSockets in sun.nio.ch.PipeImpl for the Selector wakeup pipe on Windows (JEP 380), and the wepoll Selector that drives it became default in JDK 17 (JDK-8266369, see JDK 17 release notes). The AF_UNIX path in PipeImpl was "temporarily" disabled in JDK-8280233 and later re-enabled, so whether a given JDK 17.x build triggers depends on whether its PipeImpl currently uses AF_UNIX or TCP for the wakeup pipe. The socket path is derived from TEMP; /tmp is not a valid Windows path, so connect0 returns EINVAL. Verified on JDK 21 and JDK 25.

The Cygwin manual (Environment Variables) calls out exactly this risk:

TMPDIR, TMP, TEMP are converted to UNIX format … will be used by some Cygwin applications, possibly with unexpected results.

Microsoft staff have confirmed on MS Q&A 5599711 that MSIX AppContainer does not formally support Java NIO selectors / Unix-domain sockets — though that thread is about JVM apps packaged as MSIX, not env-leak to child JVMs.

Per-call workaround

unset TEMP TMP; ./gradlew <task>

What does NOT work

  • Setting "env": { "TEMP": "...", "TMP": "..." } in ~/.claude/settings.json — MSIX still overrides or the env field doesn't reach Bash subprocesses.
  • -Djava.nio.channels.spi.SelectorProvider=sun.nio.ch.WindowsSelectorProvider — JDK 21+ ignores it; the legacy provider has been removed.
  • Setting javaToolchain in build.gradle.kts to a JDK without AF_UNIX in PipeImpl (e.g., JDK 11). The JVM that fails the Selector.open() is the Gradle daemon JVM (org.gradle.java.home / JAVA_HOME), not the project's toolchain. The daemon forks tasks; toolchain only affects compile/test JVMs invoked by the daemon.

Secondary problem: Gradle daemon poisoning

unset TEMP TMP only fixes the current shell. If the first ./gradlew that spawns a daemon JVM has the leaked env, the daemon (3-hour idleTimeout) captures TMP=/tmp and holds it. Every subsequent caller — including plain .bat from Explorer with clean Windows env — attaches to the poisoned daemon, and its forked tasks (:run, test runner) inherit the bad TMP.

Verify by reading ~/.gradle/daemon/<version>/daemon-<pid>.out.log and looking for the env dump line:

EstablishBuildEnvironment Configuring env variables: [..., MSYSTEM=…, CLAUDECODE=…]

Presence of MSYSTEM / CLAUDECODE ⇒ daemon spawned from the Claude Desktop shell ⇒ poisoned.

Fix:

./gradlew --stop          # or, if hung: taskkill /F /IM java.exe
unset TEMP TMP
./gradlew <task>          # daemon respawns with clean env

Asks

  1. Fix in Claude Desktop: make the bundled Bash not leak Cygwin-style TEMP / TMP to child processes, or at minimum document the gotcha for users running JVM tooling. (An MSI installer is not offered, so this cannot be worked around by switching package format.)
  2. If the env field in settings.json is intended to scrub subprocess env, fix it — currently it has no effect on Bash subprocess env on MSIX installs.
  3. If this repo is the wrong place to report Claude Desktop bugs, please point to the right channel.

Related

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING