Preflight Checklist
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
Esc to 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:
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 process
recvfrom() → EAGAIN — blocked 2-4s waiting for child
recvfrom() → "C:\\Users\\<username>" — Windows home path
SIGCHLD — child process exits
recvfrom() → "/mnt/c/Users/<username>" — wslpath result
read() → 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 —
USERPROFILE doesn'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) or wslvar USERPROFILE from wslu are 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
/plugin and press Esc to 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) — repeated powershell.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
Preflight Checklist
What's Wrong?
Claude Code on WSL2 freezes for 20-40 seconds in specific situations:
Escto back out of/plugin,/model, etc. (~2-3s, single call)/exit— ~2-3s hang before session closes (single call)The root cause is repeated uncached
powershell.exeinvocations 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:
Root Cause Analysis
1. System diagnostics ruled out common WSL2 issues:
2.
stracereveals repeated interop calls with 2.5-4.4s gaps: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 data3.
strace -f -e trace=execveidentifies the exact command:Timing:
4. This repeats 7+ times in 30 seconds with identical results:
C:\Users\<username>C:\Users\<username>C:\Users\<username>C:\Users\<username>C:\Users\<username>C:\Users\<username>C:\Users\<username>7 calls × ~2.7s avg = ~19s of freeze in 30 seconds.
The Problem
cmd.exe /c echo %USERPROFILE%is ~0.3s; environment variable reads are instantSuggested Fix
The core issue is lack of caching, not the choice of PowerShell itself. The same immutable value is fetched 7+ times per session.
USERPROFILEdoesn't change during a session. Call once, store, reuse. This alone eliminates the problem regardless of how the value is fetched.WSLENV,/proc/*/environ), which would avoid interop entirely.cmd.exe /c echo %USERPROFILE%(~0.3s) orwslvar USERPROFILEfromwsluare significantly faster than PowerShell.-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.exethat intercepts the$env:USERPROFILEquery:With
export PATH="$HOME/bin:$PATH"in~/.bashrc, this eliminates the freeze entirely.What Should Happen?
Claude Code should resolve
$env:USERPROFILEonce 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
claude/pluginand pressEscto back out — ~2-3s freeze (single call)/exit— ~2-3s delay before terminating (single interop call)sudo strace -f -tt -T -e trace=execve -p $(pgrep claude)— repeatedpowershell.exe -Command "$env:USERPROFILE"calls visible during each freezeClaude 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