Windows MSIX: stealth auto-update leaves app unlaunchable with 0x80070020 until reboot (non-PTY children keep the AppX container and Helium User.dat hive mounted)

Status Open
Maintainer reply None cached
Activity 3 comments · opened Sep 4, 2026

Summary

A stealth (idle-timeout) auto-update of the MSIX-packaged Windows desktop app can leave the app permanently unlaunchable until a reboot. Every launch fails with 0x80070020 (ERROR_SHARING_VIOLATION).

Root cause: beforeQuitForUpdate only kills PTY process trees. Non-PTY child processes spawned by claude.exe (node, cmd, powershell, ssh, MCP servers) inherit the package identity and stay inside the Desktop AppX container. As long as one survives, the container is never destroyed, so the app-silo registry hive …\SystemAppData\Helium\User.dat stays mounted at \REGISTRY\WC\Silo<guid>user_sid. The newly registered package version then creates a new container and calls RegLoadKey on the same User.dat file, which fails with a sharing violation.

Environment

  • Windows 11 Pro 10.0.26200
  • Claude desktop app, MSIX (Claude_pzs8sxrjxfjjc), Developer signature kind
  • Version at failure: upgrading 1.44121.4.0 -> 1.46388.2.0
  • Packaged service present: CoworkVMService (app\resources\cowork-svc.exe, LocalSystem, auto-start)

Symptoms

  • App does not start. Windows shows a "file is in use by another program" style error.
  • No Claude.exe processes are running.
  • Microsoft-Windows-AppModel-Runtime/Admin:
  • Event 215: 0x80070020: cannot create Desktop AppX container for package Claude_1.46388.2.0_x64__pzs8sxrjxfjjc
  • Event 208: 0x80070020: cannot create process for package Claude_1.46388.2.0_x64__pzs8sxrjxfjjc [LaunchProcess]
  • Process Monitor shows RegLoadKey / CreateFile on

C:\Users\<user>\AppData\Local\Packages\Claude_pzs8sxrjxfjjc\SystemAppData\Helium\User.dat
returning SHARING VIOLATION, issued by svchost.exe (Appinfo service) — the victim, not the holder.

  • The hive remains listed in HKLM\SYSTEM\CurrentControlSet\Control\hivelist under \REGISTRY\WC\Silo<guid>user_sid.

Timeline from the logs (single reproduction, times local)

  1. 16:33:33 boot. 1.44121.4.0 running: cowork-svc.exe in container A, claude.exe (Electron main) in container B.
  2. 16:35:32 AppXDeploymentServer event 400: 1.46388.2.0 staged successfully. Event 658: "Marking package for deferred registration because 1.44121.4.0 is still running". Event 638 lists running app Claude_pzs8sxrjxfjjc!Claude.
  3. 18:40:37 app main.log:

``
[stealth-update] Triggering stealth update after idle timeout
[stealth-relaunch] Saved z-order anchor / navigation history
[CCD] Killing 4 PTY process tree(s) on quit
``

  1. 18:40:38 main.log: beforeQuitForUpdate handler fired, going down for update.

AppXDeploymentServer event 603: RegisterByPackageFamilyName, options ForceApplicationShutdownOption.

  1. 18:41:09 AppModel-Runtime event 217 removes container A (the packaged service's container). Container B — the claude.exe container of the old version — is never removed. First 215/208 0x80070020 fires in the same second. The stealth relaunch never completes; main.log has no further entries until after the reboot.
  2. 18:41–19:14 71 further 215/208 events across manual launches and Windows' own RepairAppRegistrationOption re-registrations. Each re-registration terminates CoworkVMService successfully (events 9648/9650, result 0x0) and the launch still fails — the service is not the holder.
  3. 19:15:28 on shutdown, event 217 finally removes container B for 1.44121.4.0, 2 h 41 min after the app "quit".
  4. 19:16:11 boot. App launches normally.

Why the obvious diagnostics mislead

  • handle.exe finds no open handle on User.dat. A silo-mounted hive is a kernel CmLoadKey reference on the silo object, not a user-mode file handle, so absence of handles proves nothing.
  • Stopping CoworkVMService does not help, and the deployment stack already terminates it during repair registration (result 0x0) while launches keep failing.
  • NtUnloadKey on the silo path returns C000003B (STATUS_OBJECT_PATH_SYNTAX_BAD) — a path-syntax error, not evidence of a lock.

Confirmation that non-PTY children stay in the container

In a healthy session, the app container holds these descendants of claude.exe, none of which are PTY trees:

node.exe       <- cmd.exe    <- claude.exe <- claude.exe (main)
node.exe       <- cmd.exe    <- node.exe   <- cmd.exe <- claude.exe <- claude.exe (main)
powershell.exe <- claude.exe <- claude.exe (main)
conhost.exe    <- claude.exe <- claude.exe (main)

Any of these outliving beforeQuitForUpdate keeps the Desktop AppX container, and therefore the hive, alive.

Steps to reproduce

  1. Run the packaged app; let it spawn a long-lived child process that is not part of a PTY tree (an SSH connection, an MCP server, a detached background shell).
  2. Let the auto-updater stage a newer version while the app is running, so registration is deferred (event 658).
  3. Leave the app idle until the stealth update fires; it quits and force-registers the new version.
  4. Launch the app. It fails with 0x80070020 and keeps failing until a reboot.

Impact

The app is unusable until reboot. No user-mode remedy exists: repair-registration, stopping the packaged service, and manual hive unloading all fail. The failure window is invisible to the user until they try to launch.

Suggested fixes

  • On beforeQuitForUpdate, terminate the whole package process tree, not just PTY trees — or wait for the Desktop AppX container to be torn down (poll for AppModel-Runtime event 217 / container destruction) before triggering registration.
  • Before the stealth relaunch, verify that no processes remain in the package container; if any do, defer the update to the next opportunity instead of force-registering.
  • Detect 0x80070020 on launch and surface an actionable message ("restart Windows to finish the update") instead of a generic file-in-use error.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗