{"slug": "fix-claude-desktop-on-windows-self-corrupting-needsremediation", "title": "Fix Claude Desktop on Windows (self-corrupting NeedsRemediation)", "summary": "A developer named Cheng has released a PowerShell script to fix Claude Desktop on Windows, which repeatedly self-corrupts due to a bundled DLL being blocked by Code Integrity enforcement. The script deploys the non-MSIX Squirrel build outside the MSIX container, avoiding the signing issue, and optionally locks the version, removes the broken MSIX package, and migrates user data.", "body_md": "|\n<# |\n|\n.SYNOPSIS |\n|\nFixes Claude Desktop on Windows that keeps breaking with |\n|\n\"This app can't open\" / silently quits on launch. |\n|\n|\n|\n.DESCRIPTION |\n|\nNewer Claude Desktop MSIX (Store) builds bundle vk_swiftshader.dll. |\n|\nOn machines that enforce Code Integrity, Windows blocks that DLL inside |\n|\nthe MSIX container (event 3033: \"did not meet the Microsoft signing level |\n|\nrequirements\"). That crashes the GPU process (0x060C201E), which makes |\n|\nWindows flag the whole package as \"Modified, NeedsRemediation\", so the |\n|\nnext launch fails with 0x3CFC. Repair/reinstall only fixes it for a few |\n|\nminutes, then it self-corrupts again. |\n|\n|\n|\nThis script sidesteps the bug by deploying the NON-MSIX (Squirrel) build |\n|\nto %LOCALAPPDATA%\\AnthropicClaude, which lives OUTSIDE the MSIX container, |\n|\nso the signing enforcement never applies. Same Claude, no self-corruption. |\n|\n|\n|\nWhat it touches (full transparency): |\n|\n- Downloads an official Squirrel .nupkg from downloads.claude.ai |\n|\n- Extracts it to %LOCALAPPDATA%\\AnthropicClaude\\app-<version>\\ |\n|\n- Creates Desktop + Start Menu shortcuts to Claude.exe |\n|\n- Registers the claude:// protocol handler (HKCU) so OAuth / Google |\n|\nsign-in redirects back into the app instead of looping forever on |\n|\nthe sign-in screen |\n|\n- (optional -Lock) adds \"0.0.0.0 downloads.claude.ai\" to the hosts file |\n|\nto stop it auto-updating back into the broken MSIX build (needs admin) |\n|\n- (optional -RemoveBadMsix) uninstalls the broken MSIX package |\n|\n- (optional -MigrateData) copies your old MSIX chat/session data over |\n|\nso your history isn't \"wiped\" after the switch |\n|\n|\n|\nIt does NOT delete your Claude data / sign-in (that lives elsewhere). |\n|\n|\n|\n.PARAMETER Version |\n|\nSpecific version to install, e.g. 1.22209.3. Omit to auto-detect the |\n|\nlatest from the RELEASES manifest. |\n|\n|\n|\n.PARAMETER Lock |\n|\nAlso lock the version via the hosts file so Claude can't auto-update back |\n|\ninto the broken MSIX build. Requires an elevated (Administrator) shell. |\n|\n|\n|\n.PARAMETER RemoveBadMsix |\n|\nAlso uninstall the broken MSIX (Store) package. |\n|\n|\n|\n.PARAMETER MigrateData |\n|\nCopy your existing chat/session data from the old MSIX package folder |\n|\ninto the new build so your history carries over (the two builds use |\n|\ndifferent data directories, so a fresh switch otherwise looks \"wiped\"). |\n|\n|\n|\n.EXAMPLE |\n|\n.\\Fix-ClaudeDesktop.ps1 |\n|\nDeploy the latest Squirrel build and make shortcuts. |\n|\n|\n|\n.EXAMPLE |\n|\n.\\Fix-ClaudeDesktop.ps1 -Lock -RemoveBadMsix |\n|\n(Run as Admin) Deploy, remove the broken MSIX, and lock the version. |\n|\n|\n|\n.EXAMPLE |\n|\n.\\Fix-ClaudeDesktop.ps1 -Version 1.22209.3 |\n|\nPin a specific known-good version. |\n|\n|\n|\n.NOTES |\n|\nAuthor : Cheng (building Wuwei - wuweiai.io) |\n|\nLicense : MIT. Use at your own risk, no warranty. |\n|\nThe manual steps are documented in the thread; this just automates them. |\n|\n#> |\n|\n|\n|\nparam( |\n|\n[string]$Version = \"\", |\n|\n[switch]$Lock, |\n|\n[switch]$RemoveBadMsix, |\n|\n[switch]$MigrateData # copy old MSIX chat/session data into the new build |\n|\n) |\n|\n|\n|\n$ErrorActionPreference = \"Stop\" |\n|\n|\n|\n# TLS 1.2 for older Windows/PowerShell so the download doesn't fail |\n|\ntry { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } catch {} |\n|\n|\n|\n$base = \"https://downloads.claude.ai/releases/win32/x64\" |\n|\n$root = Join-Path $env:LOCALAPPDATA \"AnthropicClaude\" |\n|\n$hostFile = \"$env:SystemRoot\\System32\\drivers\\etc\\hosts\" |\n|\n|\n|\nfunction Info($m){ Write-Host \"[*] $m\" -ForegroundColor Cyan } |\n|\nfunction Good($m){ Write-Host \"[OK] $m\" -ForegroundColor Green } |\n|\nfunction Warn($m){ Write-Host \"[!] $m\" -ForegroundColor Yellow } |\n|\n|\n|\nfunction Test-Admin { |\n|\n$id = [Security.Principal.WindowsIdentity]::GetCurrent() |\n|\n(New-Object Security.Principal.WindowsPrincipal($id)).IsInRole( |\n|\n[Security.Principal.WindowsBuiltInRole]::Administrator) |\n|\n} |\n|\n|\n|\nWrite-Host \"\" |\n|\nWrite-Host \" Fix-ClaudeDesktop - non-MSIX Squirrel deploy\" -ForegroundColor White |\n|\nWrite-Host \" by Cheng | wuweiai.io\" -ForegroundColor DarkGray |\n|\nWrite-Host \"\" |\n|\n|\n|\n# --- 0) If a previous run locked the update host, temporarily unlock so we |\n|\n# can download. We re-lock at the end if it was locked. |\n|\n$wasLocked = $false |\n|\ntry { |\n|\nif ((Get-Content $hostFile -ErrorAction SilentlyContinue) -match 'downloads\\.claude\\.ai') { |\n|\n$wasLocked = $true |\n|\n} |\n|\n} catch {} |\n|\n|\n|\nif ($wasLocked) { |\n|\nif (Test-Admin) { |\n|\nInfo \"Version lock found in hosts; temporarily unlocking to download...\" |\n|\n(Get-Content $hostFile) | |\n|\nWhere-Object { $_ -notmatch 'downloads\\.claude\\.ai' -and $_ -notmatch 'Claude desktop lock' } | |\n|\nSet-Content $hostFile -Encoding ASCII |\n|\nipconfig /flushdns | Out-Null |\n|\nStart-Sleep -Seconds 1 |\n|\n} else { |\n|\nWarn \"hosts has a version lock (0.0.0.0 downloads.claude.ai) and you're NOT admin.\" |\n|\nWarn \"The download will fail. Re-run in an Administrator PowerShell, or remove that line first.\" |\n|\n} |\n|\n} |\n|\n|\n|\n# --- 1) Resolve version |\n|\nif (-not $Version) { |\n|\nInfo \"Detecting latest version from RELEASES...\" |\n|\n$rel = (Invoke-WebRequest -UseBasicParsing \"$base/RELEASES\").Content |\n|\nif ($rel -match 'AnthropicClaude-([0-9.]+)-full\\.nupkg') { $Version = $Matches[1] } |\n|\nif (-not $Version) { throw \"Could not detect version from RELEASES.\" } |\n|\n} |\n|\nInfo \"Target version: $Version\" |\n|\n|\n|\n# --- 2) Download the Squirrel nupkg |\n|\n$tmp = Join-Path $env:TEMP \"AnthropicClaude-$Version-full.nupkg\" |\n|\n$url = \"$base/AnthropicClaude-$Version-full.nupkg\" |\n|\nInfo \"Downloading $url\" |\n|\nInvoke-WebRequest -UseBasicParsing $url -OutFile $tmp |\n|\nGood (\"Downloaded {0} MB\" -f [math]::Round((Get-Item $tmp).Length / 1MB)) |\n|\n|\n|\n# --- 3) Extract lib/net45 -> app-<version> |\n|\n$dst = Join-Path $root \"app-$Version\" |\n|\nif (Test-Path $dst) { Remove-Item $dst -Recurse -Force } |\n|\nNew-Item -ItemType Directory -Path $dst -Force | Out-Null |\n|\n|\n|\nInfo \"Extracting to $dst\" |\n|\nAdd-Type -AssemblyName System.IO.Compression.FileSystem |\n|\n$zip = [System.IO.Compression.ZipFile]::OpenRead($tmp) |\n|\ntry { |\n|\nforeach ($e in $zip.Entries) { |\n|\nif ($e.FullName -notlike \"lib/net45/*\") { continue } |\n|\nif ($e.FullName.EndsWith(\"/\")) { continue } |\n|\n$relPath = $e.FullName.Substring(\"lib/net45/\".Length) |\n|\n$target = Join-Path $dst $relPath |\n|\n$dir = Split-Path $target -Parent |\n|\nif (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null } |\n|\n[System.IO.Compression.ZipFileExtensions]::ExtractToFile($e, $target, $true) |\n|\n} |\n|\n} finally { |\n|\n$zip.Dispose() |\n|\n} |\n|\n|\n|\n$exe = Join-Path $dst \"Claude.exe\" |\n|\nif (-not (Test-Path $exe)) { throw \"Claude.exe not found after extract - the package layout may have changed.\" } |\n|\nGood \"Deployed Claude.exe (non-MSIX)\" |\n|\n|\n|\n# --- 4) Optionally remove the broken MSIX package |\n|\nif ($RemoveBadMsix) { |\n|\n$pkg = Get-AppxPackage *claude* -ErrorAction SilentlyContinue |\n|\nif ($pkg) { |\n|\nInfo \"Removing broken MSIX package: $($pkg.PackageFullName)\" |\n|\nGet-Process | Where-Object { $_.Path -like '*WindowsApps\\Claude*' } | |\n|\nStop-Process -Force -ErrorAction SilentlyContinue |\n|\n$pkg | Remove-AppxPackage -ErrorAction SilentlyContinue |\n|\nGood \"MSIX package removed\" |\n|\n} else { |\n|\nInfo \"No MSIX Claude package found (nothing to remove).\" |\n|\n} |\n|\n} |\n|\n|\n|\n# --- 5) Shortcuts (Desktop + Start Menu) |\n|\n$ws = New-Object -ComObject WScript.Shell |\n|\nforeach ($loc in @( |\n|\n[Environment]::GetFolderPath('Desktop'), |\n|\n(Join-Path $env:APPDATA 'Microsoft\\Windows\\Start Menu\\Programs') |\n|\n)) { |\n|\n$lnk = $ws.CreateShortcut((Join-Path $loc 'Claude.lnk')) |\n|\n$lnk.TargetPath = $exe |\n|\n$lnk.WorkingDirectory = $dst |\n|\n$lnk.IconLocation = \"$exe,0\" |\n|\n$lnk.Save() |\n|\n} |\n|\nGood \"Created Desktop and Start Menu shortcuts\" |\n|\n|\n|\n# --- 5.5) Register the claude:// protocol handler (HKCU) |\n|\n# Why: after Google/OAuth sign-in completes in the browser, Windows hands |\n|\n# the result back via a claude://... callback. A hand-extracted nupkg (or a |\n|\n# broken MSIX) can leave HKCU\\Software\\Classes\\claude without a |\n|\n# shell\\open\\command, so the callback goes nowhere and the app loops on the |\n|\n# sign-in screen forever. Email + verification code works because it never |\n|\n# uses the deep-link. This points the protocol at the Claude.exe we deployed. |\n|\ntry { |\n|\n$cmd = \"`\"$exe`\" `\"%1`\"\" |\n|\nNew-Item -Path 'HKCU:\\Software\\Classes\\claude' -Force | Out-Null |\n|\nNew-ItemProperty -Path 'HKCU:\\Software\\Classes\\claude' -Name '(default)' -Value 'URL:claude' -PropertyType String -Force | Out-Null |\n|\nNew-ItemProperty -Path 'HKCU:\\Software\\Classes\\claude' -Name 'URL Protocol' -Value '' -PropertyType String -Force | Out-Null |\n|\nNew-Item -Path 'HKCU:\\Software\\Classes\\claude\\shell\\open\\command' -Force | Out-Null |\n|\nNew-ItemProperty -Path 'HKCU:\\Software\\Classes\\claude\\shell\\open\\command' -Name '(default)' -Value $cmd -PropertyType String -Force | Out-Null |\n|\nGood \"Registered claude:// protocol handler -> OAuth / Google sign-in will redirect back into the app\" |\n|\n} catch { |\n|\nWarn \"Could not register claude:// handler ($($_.Exception.Message)). Sign-in still works via email + verification code.\" |\n|\n} |\n|\n|\n|\n# --- 5.6) Verify the claude:// handler points at THIS build (self-check) |\n|\n# Confirms the registration above actually landed and points at the exe we |\n|\n# just deployed. If a stale entry points at an old app-<version> path, the |\n|\n# Google/OAuth callback lands nowhere and you loop on the sign-in screen. |\n|\ntry { |\n|\n$reg = (Get-ItemProperty 'HKCU:\\Software\\Classes\\claude\\shell\\open\\command' -ErrorAction Stop).'(default)' |\n|\nif ($reg -like \"*$dst*\") { |\n|\nGood \"Verified claude:// -> this build. Google / OAuth sign-in will land back in the app.\" |\n|\n} else { |\n|\nWarn \"claude:// points at a different path ($reg). Repointing to this build...\" |\n|\nNew-ItemProperty -Path 'HKCU:\\Software\\Classes\\claude\\shell\\open\\command' -Name '(default)' -Value \"`\"$exe`\" `\"%1`\"\" -PropertyType String -Force | Out-Null |\n|\nGood \"claude:// repointed to $exe\" |\n|\n} |\n|\n} catch { |\n|\nWarn \"claude:// handler not found after registration; sign-in may loop. Re-run the script, or sign in with email + verification code.\" |\n|\n} |\n|\n|\n|\n# --- 5.7) Optionally migrate old chat/session data |\n|\n# The MSIX (Store) build stores data under its sandboxed package folder, |\n|\n# while the Squirrel build uses %APPDATA%\\Claude. After switching, history |\n|\n# looks \"wiped\" only because the new build can't see the old sandboxed data. |\n|\n# Nothing was deleted; this copies it over. |\n|\nif ($MigrateData) { |\n|\n$newData = Join-Path $env:APPDATA 'Claude' |\n|\n$msixRoot = Join-Path $env:LOCALAPPDATA 'Packages' |\n|\n$src = $null |\n|\nif (Test-Path $msixRoot) { |\n|\n$src = Get-ChildItem $msixRoot -Directory -Filter 'Claude_*' -ErrorAction SilentlyContinue | |\n|\nForEach-Object { Join-Path $_.FullName 'LocalCache\\Roaming\\Claude' } | |\n|\nWhere-Object { Test-Path $_ } | Select-Object -First 1 |\n|\n} |\n|\nif ($src) { |\n|\nInfo \"Migrating old MSIX data:`n from $src` n to $newData\" |\n|\nif (Test-Path $newData) { |\n|\n$bak = \"$newData.bak-$(Get-Date -Format yyyyMMdd-HHmmss)\" |\n|\nCopy-Item $newData $bak -Recurse -Force -ErrorAction SilentlyContinue |\n|\nInfo \"Backed up existing new-build data to $bak\" |\n|\n} |\n|\nNew-Item -ItemType Directory -Path $newData -Force | Out-Null |\n|\nCopy-Item (Join-Path $src '*') $newData -Recurse -Force -ErrorAction SilentlyContinue |\n|\nGood \"Migrated old chat/session data. Your history should reappear after launch.\" |\n|\n} else { |\n|\nWarn \"No old MSIX data folder found under $msixRoot (nothing to migrate).\" |\n|\n} |\n|\n} |\n|\n|\n|\n# --- 6) Lock version (block auto-update) if requested, or restore a lock we |\n|\n# temporarily removed at step 0. |\n|\nif ($Lock -or $wasLocked) { |\n|\nif (Test-Admin) { |\n|\ntry { |\n|\n$lines = Get-Content $hostFile | |\n|\nWhere-Object { $_ -notmatch 'downloads\\.claude\\.ai' -and $_ -notmatch 'Claude desktop lock' } |\n|\n$lines += '# Claude desktop lock version (avoid bad MSIX auto-update)' |\n|\n$lines += '0.0.0.0 downloads.claude.ai' |\n|\nSet-Content -Path $hostFile -Value $lines -Encoding ASCII |\n|\nipconfig /flushdns | Out-Null |\n|\nGood \"Version locked via hosts. Remove that line to allow updates later.\" |\n|\n} catch { |\n|\nWarn \"Lock failed - run PowerShell as Administrator to edit hosts. (Fix still works without lock.)\" |\n|\n} |\n|\n} else { |\n|\nWarn \"Skipping version lock: not admin. (Fix still works; re-run as admin with -Lock.)\" |\n|\n} |\n|\n} |\n|\n|\n|\nWrite-Host \"\" |\n|\nGood \"Done. Launch Claude from the new Desktop shortcut.\" |\n|\nInfo \"It now runs from: $dst\" |\n|\nInfo \"That path is outside the MSIX container, so no more NeedsRemediation self-corruption.\" |\n|\nWrite-Host \"\" |\n|\n|\n|\nStart-Process $exe |", "url": "https://wpnews.pro/news/fix-claude-desktop-on-windows-self-corrupting-needsremediation", "canonical_source": "https://gist.github.com/KehuiPang/0a678ac866ed4b9b883e9a30f4b63372", "published_at": "2026-08-16 05:42:55+00:00", "updated_at": "2026-08-25 09:44:23.612465+00:00", "lang": "en", "topics": ["developer-tools"], "entities": ["Claude Desktop", "Anthropic", "Cheng", "Wuwei", "Windows", "MSIX", "Squirrel", "PowerShell"], "alternates": {"html": "https://wpnews.pro/news/fix-claude-desktop-on-windows-self-corrupting-needsremediation", "markdown": "https://wpnews.pro/news/fix-claude-desktop-on-windows-self-corrupting-needsremediation.md", "text": "https://wpnews.pro/news/fix-claude-desktop-on-windows-self-corrupting-needsremediation.txt", "jsonld": "https://wpnews.pro/news/fix-claude-desktop-on-windows-self-corrupting-needsremediation.jsonld"}}