Windows Troubleshooting from WSL
When the user reports a Windows problem (Store won't open, service failed, AppX broken) and the agent is in WSL, the workflow is: write the fix as a .ps1 on the Windows side, then have the user run it in an admin PowerShell. Pure-ASCII + /mnt/c/Users/<user>/Desktop/ is the only reliable handoff.
When to use this skill
- User says "Microsoft Store doesn't work" / "Windows service X failed" / "I get error 0x... in [Windows app]"
- User has authorized running PowerShell commands on the Windows host
- You're in a WSL session (Ubuntu on Windows) and want to fix Windows state
- The fix needs admin (Service start, AppX re-register, registry edit, sfc/DISM)
The handoff contract
WSL and Windows have independent filesystems and shells that don't auto-bridge. There is exactly one reliable pattern for "user runs a PS1 I wrote from WSL":
- Write a pure-ASCII
.ps1(no Chinese, no emoji, no===markers at line start) - Save it to
/mnt/c/Users/<user>/Desktop/<name>.ps1(visible to Windows asC:\Users\<user>\Desktop\<name>.ps1) - Give the user the exact two-line run command:
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass & "C:\Users\<user>\Desktop\<name>.ps1" - Never give them a path starting with
/tmp/...(WSL-only),\\wsl$\...(works but fragile), or any Linux path
WSL helper to drop a script to desktop (always use this instead of asking user to copy-paste):
cp /tmp/my-fix.ps1 /mnt/c/Users/Aorus/Desktop/my-fix.ps1
Hard rules (read these first, they're all learned from failures)
0. WSL→Windows privilege boundary — you are a regular user
Hard truth: when your shell is WSL (Ubuntu on Windows) and you call cmd.exe /c sc ... or powershell.exe ..., you are running as the same unelevated user as your Windows login. There is no sudo from WSL→Windows that triggers UAC. UAC is a Windows-kernel feature, and WSL processes cannot invoke it.
What this means in practice:
reg add HKLM\...from WSL →拒绝访问 / Access is deniedeven if the user is a Windows adminsc config wuauserv start= autofrom WSL →OpenService 失败 5: 拒绝访问reg query HKLM\...from WSL → may even fail (HKLM read usually works, but SYSTEM-protected subkeys deny)reagentc /infofrom WSL → access denied
Do not waste turns trying sudo cmd.exe /c ..., Start-Process -Verb RunAs (no GUI box to click), or psexec. None of them work from WSL context without the user manually doing something in the Windows UI.
The correct workflow is: detect the boundary (whoami /groups | findstr S-1-16 returns S-1-16-8192 for medium, S-1-16-12288 for high), then hand the script to the user to run in an admin PowerShell on Windows via the handoff contract below. Plan your fix as a deliverable, not as autonomous execution.
1. Pure-ASCII only inside .ps1 body
- No Chinese characters in any code line, comment, or
Write-Hostmessage - No
===markers at start of comment lines — PowerShell 5.1 tries to parse them as commands - Use
[OK] / [WARN] / [FAIL]English status tags, not colored Chinese - Use ASCII arrows (
->) not unicode (→) - Exception: file path strings and registry values can contain unicode (Windows is fine with these)
If you must include Chinese in a Write-Host for the user, force UTF-8 at script top:
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
chcp 65001 | Out-Null
…but this still doesn't fix comment-line parsing. Keep comments English.
2. Save files via WSL, not Windows Notepad
Notepad defaults to ANSI/GBK on Chinese Windows. If you give the user a script and they save it via Notepad, the Chinese gets garbled (鎴栬€呭皾璇? etc.) and the script breaks with parse errors. Solution: always write the file from WSL to /mnt/c/Users/.../Desktop/. Never ask the user to "save this to a .ps1 file via Notepad".
3. Detect partial admin early
If Set-Service -StartupType Automatic returns Access is denied but Start-Service works, the user's PowerShell is not full admin (likely UAC-restricted token, or local admin without full privileges). Two paths:
- Try
sc.exe config <svc> start= autoinstead — sometimes bypasses the PS constraint - If
sc.exealso fails, you need SYSTEM-level access; tell the user and stop
Don't loop retrying Set-Service — it'll just keep failing.
4. WSL→Windows path translation is NOT automatic
| From WSL | To Windows | Works? |
|---|---|---|
/tmp/foo.ps1 |
C:\tmp\foo.ps1 |
❌ Doesn't exist (no auto-translation) |
/mnt/c/Users/Aorus/Desktop/foo.ps1 |
C:\Users\Aorus\Desktop\foo.ps1 |
✅ |
\\wsl$\Ubuntu\tmp\foo.ps1 |
(WSL distro path) | ✅ but only with WSL installed and distro name correct |
Use /mnt/c/Users/<user>/Desktop/ always.
5. $pid is a read-only automatic variable in PowerShell
If you write a loop like foreach ($pid in $pids) { ... }, PowerShell throws 无法覆盖变量 PID,因为该变量为只读变量或常量 / Cannot overwrite variable PID. This is a script-breaking bug that looks like a logic error at first glance.
Fix: rename the loop variable. $procId or $procPid works. $PID (PowerShell's built-in for "current process ID") is reserved. Same for $?, $^, $_, $Args, $Input, $Host, $HOME, $true, $false, $null — don't reuse them as loop variables or function parameters.
6. === markers in code blocks the user pastes into PS 5.1
When the user pastes a multi-line block of code into a Win10/11 PowerShell 5.1 prompt, any line that starts with === (even inside a comment) is parsed as a command token. Symptom: === : 无法将"==="项识别为 cmdlet. This bites when you give the user a transcript-style block with section headers.
Fix options:
- Use
# ===(commented out) or[OK] / [WARN] / [FAIL]status tags in your code blocks - Or instruct the user to save the block to a
.ps1file and run it (not paste it), so PS doesn't try to parse comment-shaped text
The canonical way to deliver a multi-step script is still: write the file to /mnt/c/Users/<user>/Desktop/, give the user the 2-line Set-ExecutionPolicy ... && & "..." runner. The user pastes 2 lines, the script does the rest. Never have the user paste the whole script body.
7. Never inline PowerShell in bash strings — use .ps1 files
When calling powershell.exe from WSL bash, bash expands $_, $pid, $args, and
other $-prefixed tokens before PowerShell ever sees them. The result is garbled
script text that PowerShell can't parse:
# ❌ $_ becomes a bash artifact (e.g. /tmp/cua.Source)
powershell.exe -Command "Get-Process | ForEach-Object { $_.Name }"
# ✅ Write the script to /mnt/c/... first, then -File it
powershell.exe -ExecutionPolicy Bypass -File "C:\Users\Aorus\temp.ps1"
Single-quoted heredocs do NOT fully protect against this — bash still expands some
constructs inside command substitution and pipeline contexts. The only reliable
pattern is a .ps1 file on the Windows filesystem, executed via -File.
Diagnostic workflow
For "X Windows feature is broken", the standard read-only ladder is:
# 1. Services (which ones stopped, which are set to manual)
Get-Service <svc1>, <svc2> | Format-Table Name, Status, StartType -AutoSize
# 2. The package itself (Store, etc.)
Get-AppxPackage -AllUsers -Name <PackageName> | Format-List Name, Version, State, InstallLocation, SignatureKind
# 3. The EventLog for the actual error
Get-WinEvent -LogName "Microsoft-Windows-AppxDeploymentServer/Operational" -MaxEvents 200 |
Where-Object { $_.Level -le 3 } | Select-Object -First 15 ... | Format-List
# 4. Process holding the resource
Get-Process -Name <proc> | Format-Table Id, ProcessName, StartTime
# 5. AppX deployment log for a specific failure
Get-AppPackageLog -ActivityID <id-from-error>
Output garbled to WSL console? Add at top of script:
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
chcp 65001 | Out-Null
Microsoft Store specific fix ladder
When user says "Microsoft Store won't open / says update service not running / says initialization failed":
- Check core services —
ClipSVC,AppXSvc,AppReadinessare the relevant ones. IfClipSVCis Stopped, start it (this fixes ~80% of "initialization failed" cases). wsreset.exe— official Microsoft cache reset tool. Run as separate process:Start-Process -FilePath "$env:SystemRoot\System32\wsreset.exe" -Wait -WindowStyle Hidden- Clear local cache (not the package itself):
$env:LOCALAPPDATA\Packages\Microsoft.WindowsStore_8wekyb3d8bbwe\AC\INetCache$env:LOCALAPPDATA\Packages\Microsoft.WindowsStore_8wekyb3d8bbwe\LocalCache$env:LOCALAPPDATA\Packages\Microsoft.WindowsStore_8wekyb3d8bbwe\Settings(locked while Store running — kill it first)$env:LOCALAPPDATA\Microsoft\Windows Store
- Clean
SoftwareDistribution(WUA cache) — stopwuauserv+bitsfirst - Re-register Store package — required to fail with
0x80073D02first (means file lock) then auto-retry after kill sfc /scannow— last resort, takes 5-15 min, often needs a rebootDISM /Online /Cleanup-Image /RestoreHealth— even more last-resort, takes 15-30 min
Always re-run the Store at the end to verify: Start-Process "ms-windows-store:" then check Get-Process -Name WinStore.App.
Deeper root cause: wuauserv StartType=Disabled
If steps 1-5 above succeed (services Running, re-register OK) but the Store still reports "initialization failed", check the wuauserv state specifically:
Get-Service wuauserv | Format-List Name, Status, StartType
# StartType = Disabled → this is the real problem, not ClipSVC
wuauserv being Disabled (not just Stopped) means it was deliberately turned off — usually by a "tweak" tool, group policy, or by hand. The Store depends on it indirectly (Store license validation, app update checks, telemetry sync). When it's Disabled:
- Standard
Start-Service wuauserv→StartService 失败 1058(cannot start a disabled service) - The 7-step fix ladder above will "succeed" at each step but the Store still won't initialize, because the dependent pipeline is broken
- Fix requires flipping StartType to auto, which needs full admin:
# From admin PowerShell, NOT from WSL (see Hard rule #0) sc.exe config wuauserv start= auto sc.exe start wuauserv - If
sc.exereturnsOpenService 失败 5: 拒绝访问even from admin PS, the user has a constrained-token admin. Fallback: edit registry directly:Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\wuauserv" -Name Start -Value 2 -Type DWord -Force # Then reboot
Event ID decoder for AppX deployment failures
When the Store re-register step 5 fails, look at the AppX deployment operational log and the activity ID log for the Event ID, not the message text:
| Event ID | Meaning | Action |
|---|---|---|
| 401 | Deployment operation failed (parent) | Read the child event in the same sequence |
| 404 | Specific deployment failure with HRESULT | Look at the HRESULT for the actual error |
| 419 | 0x80073D02: 需要关闭以下应用 |
AppX package is in use — kill WinStore.App and wsappx, retry |
| 638 | 程序包未更新,因为受影响的应用仍在运行 |
Same as 419, but at the resolution stage. PID list: {Microsoft.WindowsStore_8wekyb3d8bbwe!App} |
| 672 | Dependency version too low (The package's version is lower than the required MinVersion) |
The VCLibs/UI.Xaml/.NET Native dep needs updating separately. Common after a Store version bump: Microsoft.VCLibs.140.00.UWPDesktop lags behind. Fix: install the latest VCLibs from the Store bundle, or Add-AppxPackage it from a higher-version source. |
| 702 | Cannot remove: package not installed for this user | A different user's package; skip |
| 605 | ResolvedDeferredRegistrations failed | Cascade from one of the above; look for the earliest failure in the same sequence |
Always grep the log by Event ID, not by error message text — the message changes with locale, the Event ID is invariant.
WSL2 Interop Quirks
Windows PATH is not inherited when systemd=true
If /etc/wsl.conf has [boot] systemd=true (or the [interop] section
is missing/appendWindowsPath=false), WSL does not append Windows
%PATH% directories. cmd.exe, powershell.exe, and other Windows
binaries are not found by bare name (which cmd.exe returns nothing).
Instead use full /mnt/c/... paths:
/mnt/c/Windows/System32/cmd.exe /c "echo works"
/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe -File "C:\...\script.ps1"
To check interop status: cat /proc/sys/fs/binfmt_misc/WSLInterop — should
show enabled.
WSL2 localhost forwarding is unreliable for custom ports
Windows services bound to 127.0.0.1:PORT may not be reachable from
WSL via localhost:PORT (curl exit code 7: "Connection refused").
netstat -ano on Windows confirms the port is LISTENING, but WSL2's
localhost forwarding doesn't bridge it for custom high ports.
Workaround A — Windows host gateway IP (for services bound to 0.0.0.0):
WINDOWS_HOST=$(ip route show default | awk '{print $3}') # e.g. 172.24.48.1
nc -z -w 2 $WINDOWS_HOST $PORT
Workaround B — netsh portproxy (for services bound to 127.0.0.1 only):
# On Windows (admin PowerShell)
netsh interface portproxy add v4tov4 `
listenaddress=0.0.0.0 listenport=EXTERNAL_PORT `
connectaddress=127.0.0.1 connectport=INTERNAL_PORT
# Verify
netsh interface portproxy show all
Then reach the service from WSL via $WINDOWS_HOST:EXTERNAL_PORT.
Caution: The Windows host gateway IP can change on reboot (DHCP). For
persistent services, consider pinning a static IP in .wslconfig or
using host.docker.internal if Docker Desktop is installed.
Windows PE executables are directly executable from /mnt/c/
WSL can execute Windows .exe files directly via their /mnt/c/... path
— no cmd.exe /c or PowerShell wrapper needed. stdin/stdout/stderr pipes
work correctly across the WSL↔Windows boundary. Verified for JSON-RPC
(MCP) communication including multi-round interaction and base64-encoded
binary payloads.
# Direct execution (preferred)
/mnt/c/Windows/System32/ipconfig.exe
/mnt/c/Users/Aorus/.cua/cua-driver.exe mcp
# subprocess from Python (works for MCP stdio)
subprocess.Popen(['/mnt/c/Users/Aorus/.cua/cua-driver.exe', 'mcp'],
stdin=PIPE, stdout=PIPE, stderr=PIPE)
\r\n in stdout is normal (Windows line endings) — json.loads() and
Python readline() handle it transparently.
Pitfalls learned the hard way
Set-Servicedoesn't work without full admin — check first, don't loop retry- Store package re-register fails with
0x80073D02— script must auto-killWinStore.Appandwsappxand retry wuauservnot starting — usually means Windows Update component is broken, not a permissions issue; needsDISMnotSet-ServiceGet-AppxPackage -AllUsersrequires admin in some Win11 builds; if you see "拒绝访问" the user isn't adminSettings/settings.datis locked while Store is open — mustStop-Process WinStore.App -Forcebefore deleting- Process management: if
pkillwould be needed, ask user first (environment safety blocks it). For your own WSL-side processes (e.g. Electron), just verify withps -efwhether cleanup is actually needed before any action - Don't loop on fixes — if a step failed twice with the same error, escalate to user with diagnostic data; don't keep patching
- Stop sign after 3 fix attempts: if a Windows-side fix failed 3 times (e.g. Store still won't open after re-register + service restart + cache clear), stop and present the user with the full diagnosis + manual options instead of trying a 4th approach. The user has the right to know the fix is no longer autonomous and to take over. This matches the user's overall preference (in their profile) for manual control when scope expands into system-level Windows changes.
Verification
After any fix:
Get-Service <key services>— confirm RunningGet-AppxPackage -AllUsers <name>— confirm State = OkStart-Process "ms-windows-store:"(or relevant app) thenGet-Process— confirm it stays up- Re-check user's original symptom by launching the app
WSLg Process Investigation
When the user reports "Remote Desktop" processes they can't kill (especially system-tray icons), suspect WSLg's msrdc.exe — not traditional mstsc.exe. See references/wslg-process-investigation.md for architecture, parent-chain tracing with Get-CimInstance Win32_Process, and auto-restart root cause analysis.
Reference files
references/microsoft-store-repair.md— complete ready-to-run script for "Microsoft Store won't open" + ActivityID-based log harvestingreferences/wslg-process-investigation.md— WSLg architecture, msrdc.exe vs mstsc.exe, process parent-chain tracing, auto-restart root causescripts/wsl-handoff.sh— bash helper that copies a .ps1 to Windows desktop and prints the run command