[BUG] Claude code wsl2 freeze issue
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Claude Code on WSL2 freezes for 20-40 seconds in specific situations:
- Startup — TUI takes 20-40s to become responsive after launch (7+ repeated calls)
- After exiting commands — e.g. pressing
Escto back out of/plugin,/model, etc. (~2-3s, single call) - On
/exit— ~2-3s hang before session closes (single call)
The root cause is repeated uncached powershell.exe invocations to resolve $env:USERPROFILE. Each PowerShell cold start takes ~2.5s due to .NET runtime and profile loading, and this is called 7+ times without caching the result.
This provides the strace-level root cause analysis that was missing from previous reports of the same issue:
- #7182 — "Incredibly slow performance (PowerShell freezes main thread)"
- #22970 — Meta issue tracking Windows/WSL freeze reports
- #9114 — "Claude Code hangs and freezes on startup in WSL 2 starting with v1.0.57"
- #4077 — "Claude Code freeze in WSL"
Root Cause Analysis
1. System diagnostics ruled out common WSL2 issues:
| Check | Result |
|-------|--------|
| DNS resolution | 0.005s ✅ |
| API connectivity (curl) | 0.26s total ✅ |
| Memory/swap | 897/15909MB, 0 swap ✅ |
| I/O wait | 0% ✅ |
| CPU | 100% idle ✅ |
2. strace reveals repeated interop calls with 2.5-4.4s gaps:
sudo strace -p $(pgrep claude) -T -tt -e trace=network,read,write
17:08:30.838093 recvfrom(24, ...) = -1 EAGAIN ← wait starts
← ⏳ 2.7s blocked
17:08:33.558024 recvfrom(22, "C:\\Users\\<username>\r\n", ...) ← Windows path received
Each cycle:
socketpair()× 2 — pipes for child processrecvfrom()→EAGAIN— blocked 2-4s waiting for childrecvfrom()→"C:\\Users\\<username>"— Windows home pathSIGCHLD— child process exitsrecvfrom()→"/mnt/c/Users/<username>"— wslpath resultread()→ config data- Repeat from step 1 (no caching)
3. strace -f -e trace=execve identifies the exact command:
[pid 9529] execve("/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0//powershell.exe",
["powershell.exe", "-Command", "$env:USERPROFILE"], ...) = 0
Timing:
17:11:21.756804 execve(powershell.exe) ← start
17:11:24.272804 exited with 0 ← 2.5s later
4. This repeats 7+ times in 30 seconds with identical results:
| Gap start | Gap end | Duration | Result (always identical) |
|-----------|---------|----------|--------------------------|
| 30.838 | 33.558 | 2.7s | C:\Users\<username> |
| 34.530 | 36.959 | 2.4s | C:\Users\<username> |
| 37.992 | 40.685 | 2.7s | C:\Users\<username> |
| 41.710 | 44.245 | 2.5s | C:\Users\<username> |
| 45.266 | 48.141 | 2.9s | C:\Users\<username> |
| 49.159 | 53.598 | 4.4s | C:\Users\<username> |
| 54.642 | 57.265 | 2.6s | C:\Users\<username> |
7 calls × ~2.7s avg = ~19s of freeze in 30 seconds.
The Problem
- No caching — result is always identical but never cached
- PowerShell is the heaviest option —
cmd.exe /c echo %USERPROFILE%is ~0.3s; environment variable reads are instant - Repeats on every state transition — startup, exiting menus, termination all trigger fresh calls
Suggested Fix
The core issue is lack of caching, not the choice of PowerShell itself. The same immutable value is fetched 7+ times per session.
- Cache the result after the first call —
USERPROFILEdoesn't change during a session. Call once, store, reuse. This alone eliminates the problem regardless of how the value is fetched. - Read from environment variables directly — WSL already exposes Windows environment info (e.g.
WSLENV,/proc/*/environ), which would avoid interop entirely. - If interop is required, use a lighter method —
cmd.exe /c echo %USERPROFILE%(~0.3s) orwslvar USERPROFILEfromwsluare significantly faster than PowerShell. - If PowerShell must be used, add
-NoProfile— profile loading accounts for most of the ~2.5s overhead.powershell.exe -NoProfile -Command "$env:USERPROFILE"would be much faster.
Current Workaround
A shell shim at ~/bin/powershell.exe that intercepts the $env:USERPROFILE query:
#!/bin/bash
for arg in "$@"; do
if [[ "$arg" == '$env:USERPROFILE' ]]; then
echo "C:\Users\"
exit 0
fi
done
exec /mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0/powershell.exe "$@"
With export PATH="$HOME/bin:$PATH" in ~/.bashrc, this eliminates the freeze entirely.
What Should Happen?
Claude Code should resolve $env:USERPROFILE once and cache the result, or use a lighter-weight method (environment variable, cmd.exe). Startup, command transitions, and exit should complete without noticeable delay on WSL2.
Error Messages/Logs
Steps to Reproduce
- Install Claude Code natively on WSL2 (Ubuntu)
- Run
claude - Observe 20-40 second freeze during startup before TUI becomes responsive
- After TUI loads, run
/pluginand pressEscto back out — ~2-3s freeze (single call) - Run
/exit— ~2-3s delay before terminating (single interop call) - Verify with
sudo strace -f -tt -T -e trace=execve -p $(pgrep claude)— repeatedpowershell.exe -Command "$env:USERPROFILE"calls visible during each freeze
Note: The severity of the freeze depends on each system's PowerShell cold start time. Our environment reports ~2.5s per call (profile loading alone: ~2492ms). Systems with lighter PowerShell profiles may experience shorter delays, but the unnecessary repeated calls remain inefficient regardless. You can check your PowerShell load time with: time powershell.exe -Command '$env:USERPROFILE'
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.50 (Claude Code)
Platform
Anthropic API
Operating System
Windows
Terminal/Shell
WSL (Windows Subsystem for Linux)
Additional Information
- Operating System: Windows 11 + WSL2 (Ubuntu 24.04)
- CPU: Intel i5-13600KF
- RAM: 16GB allocated to WSL2
- Installation: Clean install, no plugins or extensions
- PowerShell profile load time: ~2500ms (reported by PowerShell itself)
- This affects all Claude Code WSL2 users — the interop overhead is inherent to WSL2's Hyper-V architecture
- Severity scales with PowerShell profile complexity and Windows system load
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗