Windows desktop: stealth update leaves orphaned processes holding old AppX container; new version unlaunchable (0x80070020) until reboot

Status Open
Maintainer reply None cached
Activity 6 comments · opened Aug 26, 2026

## Summary

The Windows desktop app's stealth auto-update leaves orphaned child processes that keep the OLD version's Desktop AppX container alive. Every launch of the NEW version then fails with 0x80070020 ("error encountered converting the job") until the machine is rebooted. The app appears simply dead to the user: clicking the icon does nothing.

This machine has hit the "update leaves Claude unlaunchable until reboot" state repeatedly across multiple update cycles (an earlier episode instead showed a "Another program is currently using this file" installer dialog while popped-out session processes were still running).

Environment

  • Windows 11 Home 10.0.26200
  • Claude desktop app (MSIX, package family Claude_pzs8sxrjxfjjc) updating 1.34493.1.0 -> 1.37937.0.0
  • Multiple Claude Code sessions / popout panes typically open at update time

Timeline (from Windows event logs, 2026-08-25, all times local)

  1. 13:37 - AppXDeploymentServer: Add operation for 1.37937 with DeferRegistrationWhenPackagesAreInUse; event 658 "Marking package for deferred registration because Claude_1.34493.1.0 is still running". Normal.
  2. 19:57:18 - RegisterByPackageFamilyName with ForceApplicationShutdownOption. Old version's container destroyed, registration finished successfully at 19:57:51 (event 400).
  3. 19:57:51 onward - every relaunch of the new version logs (AppModel-Runtime/Admin):
  • event 211 "Added process <pid> to Desktop AppX container ..." (main process starts)
  • then repeated event 215 0x80070020: Cannot create the Desktop AppX container for package Claude_1.37937.0.0 because an error was encountered converting the job.
  • then event 208 0x80070020: Cannot create the process ... error encountered while configuring runtime. [LaunchProcess]

The app dies silently. This repeats identically at 20:01:31, 20:01:32, 20:01:51, 20:02:11, 20:02:38 (including two automatic re-register/repair passes, both of which "finished successfully" per event 400 yet launches kept failing).

  1. At this point no process named claude.exe existed (verified by an external kill script that logged taskkill /IM claude.exe -> "process not found"). Something else - an orphaned child (conhost/node/shell) without package identity - was evidently still a member of the old container's job: at 20:03:02, during shutdown, the system logged event 217 destroying a Desktop AppX container for the old package 1.34493.1.0 that had been alive the whole time.
  2. Reboot at 20:03:22; first launch at 20:03:47 succeeded instantly.

Analysis

  • beforeQuitForUpdate / stealth update takes down the main app, but child processes spawned by Claude Code sessions (shells, MCP servers, console hosts) can outlive it. They hold membership in the old version's AppX container job.
  • The new version's container creation then fails at job conversion with 0x80070020 until that job is gone. Since these survivors have no package identity and are not named claude.exe, neither Task Manager's "Claude" group nor tasklist /apps surfaces them; a reboot is the only user-discoverable cure.
  • Suggested fix: before (or after) quitForUpdate, enumerate and terminate all processes in the app's own container/job (the app owns the job handle, so it can do this reliably), or make the post-update launcher detect 0x80070020 and clean up the stale container instead of dying silently.

Repro conditions

Not 100% deterministic, but has occurred repeatedly on this machine: several Claude Code sessions with popout panes + background child processes open, stealth update fires after idle timeout, swap succeeds, relaunch fails until reboot.

View original on GitHub ↗

6 Comments

J3TH4CK · 5 days ago

Corroborating forensics from a second machine — same package family, same 1.34493.1.0 → 1.37937.0.0 transition — plus two findings that may help whoever fixes this:

Environment: Windows 11 Pro 10.0.26200, Claude_pzs8sxrjxfjjc, multiple Claude Code sessions open at update time.

Timeline (local time, from event logs):

  • 2026-08-25 16:50:49 — SCM event 7040: service Claude (CoworkVMService) start type changed auto→disabled; 7045: service (re)installed from the Claude_1.37937.0.0 path.
  • 16:50:50 — AppModel-Runtime events 208/215: 0x80070020: Cannot create the Desktop AppX container for package Claude_1.37937.0.0 … an error was encountered converting the job. App icon dead from here on ("Another program is currently using this file" dialog).
  • 2026-08-26 00:29:35, 00:32:30, 00:33:03 — three more launch attempts; each repeats the same triple: SCM 7040 (auto→disabled) + 7045 (service reinstall) + AppModel 208/215 (0x80070020). The launcher reinstalls the packaged service on every attempt without stopping the running instance first.
  • 00:33:03 — AppXDeploymentServer event 400: Register of 1.37937.0.0 finished successfully — yet the launch attempts at that very second still failed with 208/215 (successful re-registration does NOT clear the pinned container).
  • 00:40:13 — System event 1074: user-initiated restart; boot completes 00:40:54.
  • 00:41:54 — app launches normally, only after the reboot. Consistent with the other reports: reboot is what releases the pinned container job.
  • 00:42:36 — the next update (1.37937.1.0) is already staged with DeferRegistrationWhenPackagesAreInUse (event 658: "deferred registration because 1.37937.0.0 is still running") — the same trap is armed for the next quit/relaunch.

(Correction: the first version of this comment claimed recovery happened without a reboot. That was wrong — the boot record shows a user-initiated restart at 00:40, which is what cleared the lock.)

Finding 1 — the packaged service is stoppable by any authenticated user; a mitigation path exists today. sc sdshow CoworkVMService returns:

D:(A;;CCLCSWRPWPDTLOCRRC;;;AU)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;S-1-5-80-…)

The first ACE grants Authenticated Users RP (start) and WP (stop); the DACL only reserves delete/reconfigure for the service SID. So the updater — or the app itself at quit time, unelevated — could stop CoworkVMService (and reap any surviving processes of the outgoing package version, e.g. chrome-native-host.exe or elevated Claude Code children — cf. #84792, #73107, #53247) before completing deferred registration and relaunching. Reports that "admin can't touch the service" concern delete/config, not stop. Whether a service stop alone releases the pinned container job (vs. also needing the orphan reap) is untested on this machine — #80286 suggests stop alone may not suffice once the silo is wedged.

Finding 2 — the manifest declares the service with a named-pipe trigger, windows.service + localSystemServices/packagedServices capabilities and <desktop6:DataItem Value="\pipe\cowork-vm-service" />, so the service can be demand-started after the swap; keeping it alive across the package transition is what pins the old container job and produces ERROR_SHARING_VIOLATION at container-job conversion.

Cross-refs with the same signature: #89687, #84435, #80286, #73694. This has been reported since March/April (#45489, #47877, #36466, #48003) — all four were auto-closed by the dedupe bot and locked while the defect stayed alive; please keep this one open.

J3TH4CK · 4 days ago

Field update — the obvious mitigation does NOT work. Stopping CoworkVMService + killing package processes at failure time was tried automatically 4× during a real occurrence today and the launch kept failing; only a reboot recovered.

Since my earlier comment I had installed an automated mitigation on this machine: a scheduled task triggered by AppModel-Runtime event 215 that (a) kills any process still running from an old WindowsApps\Claude_* directory and (b) stops CoworkVMService (permitted to Authenticated Users by the service DACL, per my earlier comment). Today it met a real occurrence:

  • 13:45 — 1.37937.2.0 staged; event 658 deferred registration (1.37937.0.0 running).
  • 17:12:38 — user quit the app; the .0.0 → .2.0 update executed and registered (events 855, 400).
  • 17:13:13 → 17:21:22 — every relaunch failed with 208/215 0x80070020 "error … converting the job" for Claude_1.37937.2.0.
  • The mitigation fired on each burst (4 runs). Its logs show: zero processes running from ANY WindowsApps\Claude_* directory at failure time, and Stop-Service CoworkVMService succeeded on every run — yet the very next launch failed identically. (An external agent attempting manual recovery in parallel also failed.)
  • 17:24 — reboot; app fine since.

So at failure time, whatever pins the old container job is not a visible package-path process, and it is not (only) the running service — stopping the service demonstrably does not release the wedged job/silo. This kills the simplest in-app fix ("stop the service before registering") as a complete solution, though it may still be necessary.

Post-recovery baseline on this machine: the Cowork VM runs as vmmem + vmwp — Host Compute System processes living outside the package directory, invisible to any package-path-based cleanup, and not children of the app. Whether that compute system survives app quit and is what pins the container silo is the open question — it would be consistent with the HCS shutdown failures reported in #83494. I have instrumented the next occurrence: on event 215 the machine now captures a full inventory (package processes, claude/cowork-svc/chrome-native-host/vmmem/vmwp process list, hcsdiag list under an elevated task, service state) before attempting anything. I will post that forensic snapshot when the bug next fires.

For the fix: whatever quit-time/update-time path Anthropic implements, it apparently must also tear down the Cowork HCS compute system (and verify the container silo is actually gone), not merely stop CoworkVMService.

maguro29dev · 3 days ago

Independent reproduction of the same failure signature, adding one data point this thread doesn't have yet: an external recovery script that killed every process named claude, deleted the Electron singleton lockfiles, and restarted CoworkVMService did not recover the app — only a full reboot did (both occurrences).

Environment

  • Windows 11 Home 10.0.26200 (same build as OP)
  • Package family Claude_pzs8sxrjxfjjc, updating 1.37937.2.0 → 1.37937.3.0
  • Several Claude Code sessions had been open earlier that morning

Timeline (2026-08-27, local time)

  • 09:03 — AppXDeploymentServer event 658: 1.37937.3.0 staged, "Marking package for deferred registration because Claude_1.37937.2.0 is still running"
  • 10:39:29 — main.log: [stealth-update] Triggering stealth update after idle timeoutbeforeQuitForUpdate handler fired, going down for update → log ends and never resumes
  • 10:39:31 — the very first relaunch attempt already fails: AppModel-Runtime/Admin event 215 0x80070020: Cannot create the Desktop AppX container for package Claude_1.37937.3.0_x64__pzs8sxrjxfjjc because an error was encountered converting the job. followed by event 208 ([LaunchProcess] fails with the same code)
  • 10:47–10:56 — 10 manual launch attempts. TWinUI event 1621 records every one as successful; AppXDeploymentServer re-runs Register on every attempt and event 400 reports "finished successfully" every time; each attempt produces the same 215/208 0x80070020 pair and no app process ever appears
  • 10:59 — reboot; next launch is clean

What did not recover it (run between the manual launch attempts):

  • Stop-Process -Force on every process named claude (both the WindowsApps path and the bundled claude-code path)
  • deleting %APPDATA%\Claude\lockfile and the LocalCache copy
  • restarting CoworkVMService (elevated)
  • the implicit re-registers triggered by each activation (Register "succeeds", launch still fails with 0x80070020)

No crash dumps (Crashpad reports empty) and no Application-log errors from the app itself — the new version simply never gets a process off the ground. This is consistent with the orphaned-job analysis in the OP and with the helper-process root-cause trace in #89648: whatever survives isn't named claude, so name-based kills don't clear the old container's job.

Prior occurrence on the same machine: 2026-08-21, updating 1.34493.0.0 → 1.34493.1.0 — main.log ends at 15:28:26 with the same beforeQuitForUpdate sequence; 4 manual launches over the following ~3 hours all no-op'd (same repeated-Register pattern in AppXDeploymentServer); recovered by the machine's scheduled 05:00 reboot the next morning.

Happy to pull more log detail from this machine if useful.

J3TH4CK · 2 days ago

Forensic snapshot captured during a live occurrence, as promised — the pinner is identified: an orphaned chrome-native-host.exe from the previous container epoch, living in the package's AppData profile (not the install dir). Sign-out cured it without a reboot. The Cowork-VM hypothesis is dead: hcsdiag list was EMPTY during the failure.

Timeline 2026-08-28 (local, Windows 11 Pro 26200, same machine as my comments above):

  • 11:20–11:25 — 1.40609.0.0 staged; event 658 deferred registration (1.37937.3.0 running).
  • ~11:44 — app exited (updater-driven); 11:44:59–11:45:00 — register .37937.3.0 → .40609.0.0 finished successfully (events 855/400).
  • 11:45:00 and 11:50:30 — relaunches fail with 208/215 0x80070020 "error … converting the job" for 1.40609.0.0.
  • Instrumented task snapshot at BOTH failures:
  • Only process under the install dir: a cowork-svc.exe freshly spawned by the launch attempt itself. It was killed and CoworkVMService stopped successfully — did not cure.
  • hcsdiag list (elevated): empty — no Host Compute System VMs existed, ruling out the Cowork VM as the pinner.
  • chrome-native-host.exe PID 26392, running since 2026-08-26 17:26:41 — two days and two version transitions old — at %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\ChromeNativeHost\chrome-native-host.exe. It lives in the package data profile, outside WindowsApps, so package-dir-based cleanup (mine, and apparently the updater's) never touches it.
  • 11:50:31 — my mitigation escalated to suggesting sign-out. User signed out and back in: app launched normally. No reboot. After relaunch, a fresh chrome-native-host.exe spawns ~6 s after the app starts — consistent with an app-managed helper that is never reaped on quit/update.

Conclusion (matches #84792 and #73107): the app leaves chrome-native-host.exe running after quit; as a leftover member of the old version's AppX container job/silo it blocks the new container's job conversion with ERROR_SHARING_VIOLATION until it dies. Sign-out kills session processes — hence the cure; reboot was never actually necessary.

Suggested fix: the quit/update path must terminate ChromeNativeHost (and any other helpers running from the package data profile) before completing registration, and verify the old container job is actually gone before relaunching.

My local mitigation now also reaps package-profile processes on event 215; I will report whether that makes the second click succeed without sign-out at the next occurrence.

jerds19 · 2 days ago

Recurred 2026-08-28, updating 1.37937.3.0 -> 1.40609.0.0 — with new evidence that the container-keeper is not any obvious process.

Same signature: after the update swap, every launch of the new version failed with event 215/208 0x80070020 ... error encountered converting the job (AppModel-Runtime/Admin, 20:20:46–20:21:13), and at shutdown (20:21:49) Windows logged destroying a Desktop AppX container belonging to the old version 1.37937.3.0 that had been alive throughout. Reboot at 20:22 fixed it; first post-boot launch succeeded instantly.

New data point: this time an external cleanup script swept aggressively between launch attempts and the failure still persisted. The sweep force-killed (a) all claude.exe, (b) every process whose command line references the package/appdata paths (caught a cmd.exe and a chrome-native-host.exe), and (c) verified via GetPackageFullName that zero remaining processes carried Claude package identity. Launches still failed with 0x80070020 afterwards.

So the old container's job is being kept alive by something with a generic name, a generic command line, and either no queryable package identity or protection from user-level OpenProcess — plausibly a RuntimeBroker/dllhost/backgroundTaskHost instance or a leaked kernel job handle. From the user side this is effectively undiagnosable and unkillable; reboot is the only recovery.

This strengthens the case that the fix needs to live in the app/updater: terminate all members of the app's own container job on quitForUpdate (the app owns the job handle), or have the post-update launcher detect 0x80070020 and tear down the stale container rather than dying silently — the user just sees a dead icon.

bugale · 1 day ago

Kernel-level mechanism confirmed + an in-place recovery that needs neither reboot nor sign-out — verified while the pinning process was still alive.

Environment: Windows 11 x64 10.0.26200, Claude_pzs8sxrjxfjjc 1.37937.1.0 → 1.37937.3.0. Update staged 08-27 05:00 (deferred registration, app running), force-registered 05:10; every launch for the next two days died with AppModel-Runtime 215/208 0x80070020, including after two RegisterByPackageFullName … RepairAppRegistrationOption passes that each "finished successfully".

What exactly stays pinned: the package's Helium registry hives, still mounted in the dead container's silo

What "error encountered converting the job" actually trips on (open question in this thread): during the failure, the package's virtual-registry hive files are exclusively locked:

  • %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\SystemAppData\Helium\User.dat
  • %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\SystemAppData\Helium\UserClasses.dat

Restart Manager names the holders as System (PID 4) and Registry (PID 468) — no user-mode process holds file handles. The hives are still loaded in the kernel registry, mounted under the OLD container epoch's silo. HKLM\SYSTEM\CurrentControlSet\Control\hivelist shows it directly:

\REGISTRY\WC\Silob857dc01-…user_sid       => …\Packages\Claude_pzs8sxrjxfjjc\SystemAppData\Helium\User.dat
\REGISTRY\WC\Silob857dc01-…user_classes   => …\Packages\Claude_pzs8sxrjxfjjc\SystemAppData\Helium\UserClasses.dat
\REGISTRY\WC\Silob857dc01-…software / …com => C:\ProgramData\Packages\Claude_pzs8sxrjxfjjc\<user-sid>\SystemAppData\Helium\Cache\<hash>.dat

Launching the new version = creating a new container = mounting those same hive files → ERROR_SHARING_VIOLATION → the "Another program is currently using this file" box (the dialog shows the current Claude.exe path only because the error surfaces from container setup — the exe itself opens fine with any sharing mode during the failure).

Consistent with the forensic snapshot above, the only surviving pre-update-epoch process on my machine was an orphaned chrome-native-host.exe (plus its cmd.exe pipe wrapper), spawned by Chrome three days before the update from %LOCALAPPDATA%\Packages\Claude_pzs8sxrjxfjjc\LocalCache\Roaming\Claude\ChromeNativeHost\. Two details that explain why kill-script mitigations reported here didn't work:

  • it lives in the package AppData profile, so anything matching processes by WindowsApps\Claude_* path misses it;
  • it holds no handles on the silo or package binaries (handle64 -a on the silo name finds nothing; only its own CWD) — it pins the silo purely by job/silo membership, so handle-hunting also misses it.

In-place recovery (no reboot, no sign-out), verified live

With SeRestorePrivilege enabled, NtUnloadKey2(…, REG_FORCE_UNLOAD) on the stale Claude entries in hivelist releases the hive files instantly; the next launch built a fresh container and the app started normally. Notably, I did not kill the pinning chrome-native-host.exe — it is still running now, harmlessly, next to the working app. So this recovery works even when the silo member can't be found at all.

Unload results for the five stale mounts:

…user_sid        normal => STATUS_SUCCESS
…software        normal => STATUS_SUCCESS
…user_classes    normal => 0xC0000121 (STATUS_CANNOT_DELETE), force => STATUS_SUCCESS
…com             normal => STATUS_SUCCESS
…com (second silo, _COM15 cache) => 0xC0000121 even with force — didn't matter, launch succeeded anyway

i.e. only the user_sid/user_classes/software/com mounts of the dead silo actually block container creation.

PoC (elevated PowerShell):

$src = @'
using System; using System.Text; using System.Runtime.InteropServices;
public static class HiveUnloader {
  [StructLayout(LayoutKind.Sequential)] public struct OA { public int Length; public IntPtr Root; public IntPtr Name; public uint Attr; public IntPtr Sd; public IntPtr Sqos; }
  [StructLayout(LayoutKind.Sequential)] public struct US { public ushort Len; public ushort MaxLen; public IntPtr Buf; }
  [DllImport("ntdll.dll")] public static extern int NtUnloadKey2(ref OA oa, uint flags);
  [StructLayout(LayoutKind.Sequential)] public struct LUID { public uint Low; public int High; }
  [StructLayout(LayoutKind.Sequential)] public struct TOKPRIV { public uint Count; public LUID Luid; public uint Attr; }
  [DllImport("advapi32.dll")] static extern bool OpenProcessToken(IntPtr p, uint acc, out IntPtr tok);
  [DllImport("advapi32.dll", CharSet=CharSet.Unicode)] static extern bool LookupPrivilegeValue(string s, string n, out LUID l);
  [DllImport("advapi32.dll")] static extern bool AdjustTokenPrivileges(IntPtr t, bool d, ref TOKPRIV tp, uint len, IntPtr pr, IntPtr rl);
  [DllImport("kernel32.dll")] static extern IntPtr GetCurrentProcess();
  public static void EnableRestorePriv() {
    IntPtr tok; OpenProcessToken(GetCurrentProcess(), 0x28, out tok);
    LUID luid; LookupPrivilegeValue(null, "SeRestorePrivilege", out luid);
    var tp = new TOKPRIV { Count = 1, Luid = luid, Attr = 2 };
    AdjustTokenPrivileges(tok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
  }
  public static int Unload(string path, bool force) {
    var b = Encoding.Unicode.GetBytes(path);
    var bp = Marshal.AllocHGlobal(b.Length + 2); Marshal.Copy(b, 0, bp, b.Length);
    var us = new US { Len = (ushort)b.Length, MaxLen = (ushort)(b.Length + 2), Buf = bp };
    var usp = Marshal.AllocHGlobal(Marshal.SizeOf(us)); Marshal.StructureToPtr(us, usp, false);
    var oa = new OA { Length = Marshal.SizeOf(typeof(OA)), Name = usp, Attr = 0x40 };
    return NtUnloadKey2(ref oa, force ? 1u : 0u);
  }
}
'@
Add-Type -TypeDefinition $src
[HiveUnloader]::EnableRestorePriv()
$hl = Get-Item 'HKLM:\SYSTEM\CurrentControlSet\Control\hivelist'
foreach ($mount in $hl.GetValueNames()) {
  if ([string]$hl.GetValue($mount) -match 'Claude') {
    $st = [HiveUnloader]::Unload($mount, $false)
    if ($st -ne 0) { $st = [HiveUnloader]::Unload($mount, $true) }
    '{0} => 0x{1:X8}' -f $mount, $st
  }
}
# then just launch the app normally

One more corroborating detail: stopping CoworkVMService does not release these hives (matches the field reports above) — and every launch attempt and every Repair pass reinstalls and restarts it (paired SCM 7045/7040 events each time, new PID each time), which is part of why "Repair" never helps.

Fix-wise this supports what's already been suggested: before registering the staged package, the updater should tear down all members of the package's container silo(s) — explicitly including Chrome-owned native-messaging hosts under LocalCache\Roaming\Claude\ChromeNativeHost — or, failing that, force-unload the package's stale \REGISTRY\WC\Silo* mounts exactly as above instead of leaving the user to reboot.