fix(apps): winget launch failure no longer crashes Apply #19
Reference in New Issue
Block a user
Delete Branch "fix/winget-launch-resilience"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Symptom (found on VM 102)
The wizard reached Apply (Apps step worked) then hard-failed:
Root cause
On IoT Enterprise LTSC, winget (App Installer) is absent, so
Process.Start("winget", "--version")throwsWin32Exceptioninstead of returning a non-zero exit code. The winget probe inEnsureWingetAsyncthrew, the exception propagated out ofInstallAsync→ out ofApplyService.RunAsync, and aborted the entire Apply. Continue-on-failure only handled non-zero exits, not launch exceptions — exactly the risk flagged in the original B1 review.Fix
AppInstalleris now fully exception-safe (it never throws):TryRunAsyncwraps everyrunner.RunAsyncand converts a launch throw into a failed run (exit -1).ResolveWingetAsyncfinds a usable winget defensively: on PATH → else bootstrap App Installer + re-probe → else the WindowsApps execution-alias path (%LOCALAPPDATA%\Microsoft\WindowsApps\winget.exe, which bare-nameProcess.Startcan't always launch). Returns the target ornull.This makes onboarding robust on offline/LTSC machines (like the test VM) and improves winget resolution on real hardware.
Test Plan
dotnet test windows/welcome/SilverOS.Welcome.sln -c Release→ 38/38 pass (2 new: probe-throws-skips-and-doesn't-crash; per-app-throw-isolated)🤖 Generated with Claude Code
On IoT LTSC winget is absent, so Process.Start('winget') throws Win32Exception ('cannot find the file specified') rather than returning non-zero. That throw propagated out of InstallAsync and failed the entire Apply ('Configuration failed'). AppInstaller is now fully exception-safe: a TryRunAsync wrapper converts launch throws into a failed run, winget is resolved defensively (PATH -> bootstrap+re-probe -> WindowsApps alias path) and when unavailable the installer skips apps and marks them not-installed instead of throwing. Per-app launch throws are isolated too. Two new tests cover probe-throws-skips and per-app-throw-isolated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>