Debugging a Failed Intune Remote Wipe

The assumption that wipe works Link to heading

Remote wipe is not a routine maintenance operation. For most organisations it is a last-resort security control, triggered the moment a device is reported lost or stolen. The assumption behind that decision is straightforward: the wipe command is sent, the device connects to the internet, the data is gone. That assumption has been silently wrong for any Windows 11 endpoint that received the May 2025 Cumulative Update.

Since that release, a broken package called UserExperience-AIX has been registered in the servicing component store on every affected endpoint. The package has no visible runtime role. Day-to-day operation is entirely unaffected. But during Push Button Reset, the mechanism Intune uses to perform a remote wipe, the CBS stack attempts to reconstruct the OS from all registered packages. It encounters the missing payload, throws an unhandled exception and crashes the reset engine. The device rolls back to its previous state, presents an error to the console and sits there with all its data intact. Intune logs the wipe as failed. Nobody gets a phone call.

As of today, fully up-to-date Windows 11 endpoints are still affected. Microsoft acknowledged a related reset and recovery regression via the Windows Release Health dashboard, introduced by the August 2025 security update and resolved that same month via an out-of-band cumulative update (KB5066189 for Windows 11 22H2/23H2). The specific UserExperience-AIX payload corruption affecting managed and custom-imaged endpoints on 24H2 and 25H2 has no associated Known Issue Rollback on record. The script-based workaround remains the only reliable fix for that variant, and without it every remote wipe attempt on an affected device fails silently.

Why this matters in practice Link to heading

Consider a concrete timeline. A laptop is reported stolen on a Monday morning. IT triggers a remote wipe at 09:05. The device connects to a coffee shop network at 10:30, receives the Intune command and the reset engine crashes. At 11:00 the device is on someone’s workbench with the previous user’s profile, saved credentials, browser sessions and locally cached files fully accessible. BitLocker protects the raw disk if it is powered off and handed to a forensics lab, but the attacker already has an authenticated Windows session. The wipe that was supposed to close that window never ran.

This is the scenario that makes a broken wipe something other than an operational inconvenience. It is an active gap in a control that security teams and incident response plans treat as reliable. Addressing it proactively, before a device is reported stolen, is straightforward with Intune Remediations.

The investigation Link to heading

The failure surfaced on a Dell Windows 11 24H2 device during a routine wipe. The device returned “There was a problem resetting your PC - No changes were made” and rolled back cleanly. BitLocker remained active throughout and no data was exposed, but a silent failure warranted a root cause investigation rather than a retry.

The first step was collecting Push Button Reset logs from C:\$SysReset\Logs\. Two independent issues were visible in setupact.log.

Root cause 1: broken CBS package Link to heading

The setuperr.log pointed to a fatal exception: HRESULT_FROM_WIN32(127) inside InitializeSmartInstaller in the CBS servicing stack, escalating to an unhandled 0xC00000E5 exception in ntdll.dll that crashed the reset engine mid-reconstruction. Error 127 is ERROR_PROC_NOT_FOUND, meaning a DLL was loaded but a required exported function was absent.

The underlying cause is 0x800f0991, PSFX_E_MISSING_PAYLOAD_FILE. The UserExperience-AIX package, introduced with the May 2025 Cumulative Update as early infrastructure for Windows Recall, had its required payload DLL missing from the WinSxS component store. When Push Button Reset attempts to reconstruct the OS, CBS tries to hydrate this package, finds no payload and crashes. The Call4Cloud team documented exactly this failure mode and established that removing the broken .mum and .cat metadata files from C:\Windows\servicing\Packages is the only reliable workaround. Running DISM /RestoreHealth does not help: it downloads the same broken package from Windows Update servers and reinstalls the problem.

The missing payload varies by build: zlib1.dll on 24H2 and snapshot_support.dll on 25H2. The same broken package also blocks the 24H2 to 25H2 feature update with the same 0x800f0991 error, so addressing it unblocks both wipe and the upgrade path.

For organisations deploying Windows from a custom image, the mechanism behind the broken state is worth understanding. When a cumulative update is injected offline into a WIM using DISM, the servicing metadata for UserExperience-AIX registers correctly, but the hydration payload does not survive the offline servicing process intact. The result is exactly the broken component state that crashes CBS during reset. This is the specific failure path for custom-imaged endpoints on 24H2 and 25H2; it is also why DISM /RestoreHealth does not help — it downloads and re-registers the same broken package from Windows Update servers.

The architectural fix for this class of problem is to stop injecting cumulative updates into the image and apply them during OOBE or Autopilot provisioning instead. Windows manages servicing operations safely at that stage and avoids the component state mismatches that offline injection can introduce. Patch My PC covers the Quality Update during OOBE feature, which makes this approach practical for managed deployments.

Root cause 2: missing storage driver in WinRE Link to heading

The same logs showed repeated 0x800703ed errors (ERROR_UNRECOGNIZED_VOLUME) when Push Button Reset attempted to resolve the volume GUID for harddisk0\partition2. The device used an NVMe drive with the BIOS storage controller set to Intel RST RAID mode. WinRE does not include the Intel RST storage driver by default, so the recovery environment cannot enumerate all disk partitions during a wipe.

This error did not independently abort the wipe on this specific device since the CBS crash came first, but it would cause failure on its own on any device where the CBS issue is resolved without addressing the driver gap. Patch My PC documented this scenario in detail. The fix is to inject the Intel RST driver from the live OS into the WinRE image using Add-WindowsDriver.

This is worth flagging beyond the specific hardware in this investigation. Any organisation deploying Windows from a custom image should assume the WinRE image inside that deployment does not carry third-party storage drivers. OEM images sometimes include them; custom-built images almost never do. On any hardware where the BIOS storage controller is set to RAID mode, which is common on Dell Latitude, Precision and OptiPlex lines with NVMe storage, WinRE cannot see the disk without the matching driver. The wipe fails before the CBS issue is even reached. If your deployment process involves building a reference image and capturing WinRE from it, injecting the relevant storage drivers into WinRE is a prerequisite for wipe reliability.

Proactive fix via Intune Remediations Link to heading

Both issues can be addressed fleet-wide using Intune Remediations (detection and remediation script pairs). The scripts run on a daily schedule under SYSTEM context, report compliance state back to Intune and self-heal if a Cumulative Update reintroduces the broken package.

A note on execution context: both remediations must run as SYSTEM. C:\Windows\servicing\Packages is owned by TrustedInstaller. Deleting the files requires taking ownership first: the remediation script calls takeown.exe and icacls.exe before each delete. Running as a local administrator produces access denied errors even with those steps; SYSTEM is the required execution context for this class of servicing operation.


CBS package: detection script Link to heading

Checks whether the UserExperience-AIX package is registered in C:\Windows\servicing\Packages and whether its required payload DLL is absent from WinSxS. The check is build-aware: zlib1.dll on 24H2, snapshot_support.dll on 25H2 and later. Reports non-compliant only when the package is registered and the build-specific DLL is missing.

#Requires -Version 5.1
<#
.SYNOPSIS
    Wipe Readiness - CBS Package Detection
    Detects broken UserExperience-AIX CBS package that causes Intune remote wipe to fail.
    Missing payload produces 0x800f0991 (PSFX_E_MISSING_PAYLOAD_FILE) during push-button
    reset reconstruction - "There was a problem resetting your PC / No changes were made."

    Exit 0 = Compliant (no remediation needed)
    Exit 1 = Non-compliant (triggers remediation)

.NOTES
    Deploy via: Intune > Devices > Remediations > Detection script
    Run as:     SYSTEM
    Run 64-bit: Yes
    Schedule:   Every 24 hours (package can reappear after cumulative updates)
    Ref:        https://call4cloud.nl/intune-remote-wipe-ending-up-in-something-went-wrong-0x800f0991/

    Build differences:
      24H2 (26100.x) - required payload: zlib1.dll
      25H2 (26200.x) - required payload: snapshot_support.dll
    Detection is build-aware; only the relevant DLL is checked per build.
#>

$ErrorActionPreference = 'Stop'
$issues = [System.Collections.Generic.List[string]]::new()

# Required payload file - build-specific
$osBuild         = [System.Environment]::OSVersion.Version.Build
$RequiredPayload = if ($osBuild -ge 26200) { 'snapshot_support.dll' } else { 'zlib1.dll' }

function Test-AixPayload {
    param([System.IO.FileInfo]$MumFile)
    $aixDirs = Get-ChildItem 'C:\Windows\WinSxS' -Directory -Filter '*userexperience-aix*' -ErrorAction SilentlyContinue
    if (-not $aixDirs) {
        $issues.Add("PACKAGE: $($MumFile.Name) - no WinSxS payload directory, wipe fails with 0x800f0991")
        return
    }
    foreach ($dir in $aixDirs) {
        if (-not (Test-Path (Join-Path $dir.FullName $RequiredPayload))) {
            $issues.Add("PACKAGE: $($MumFile.Name) - $RequiredPayload missing from $($dir.Name), wipe fails with 0x800f0991")
        }
    }
}

try {
    $pkgRoot = 'C:\Windows\servicing\Packages'
    $aixMums = Get-ChildItem -Path $pkgRoot -Filter '*UserExperience-AIX*.mum' -ErrorAction SilentlyContinue
    if ($aixMums) {
        foreach ($mum in $aixMums) { Test-AixPayload -MumFile $mum }
    }
} catch {
    $issues.Add("ERROR: CBS package check failed - $_")
}

if ($issues.Count -eq 0) {
    Write-Output "Compliant"
    exit 0
}

Write-Output "Non-compliant ($($issues.Count) issue(s)):"
$issues | ForEach-Object { Write-Output "  $_" }
exit 1

CBS package: remediation script Link to heading

First verifies that the package is actually broken: if the expected payload DLL is present in WinSxS, the package has been properly installed by a fixed cumulative update and the script exits without touching anything. If the payload is missing, it takes ownership of the .mum and .cat files using takeown.exe and icacls.exe, then removes them. Without the broken registration, CBS skips the package during OS reconstruction and the wipe completes. No reboot required.

#Requires -Version 5.1
<#
.SYNOPSIS
    Wipe Readiness - CBS Package Remediation
    Removes broken UserExperience-AIX .mum and .cat metadata from C:\Windows\servicing\Packages.
    Without the broken reference, CBS skips the package during PBR OS reconstruction.
    Skips deletion if the payload DLL is already present in WinSxS - a properly fixed
    cumulative update installs the package correctly and should not be removed.

.NOTES
    Deploy via: Intune > Devices > Remediations > Remediation script
    Run as:     SYSTEM
    Run 64-bit: Yes
    Ref:        https://call4cloud.nl/intune-remote-wipe-ending-up-in-something-went-wrong-0x800f0991/
#>

$ErrorActionPreference = 'Stop'
$fixed   = [System.Collections.Generic.List[string]]::new()
$skipped = [System.Collections.Generic.List[string]]::new()
$errors  = [System.Collections.Generic.List[string]]::new()

# Returns $true when the package metadata is present but the payload DLL is missing/broken
function Test-AixBroken {
    $aixDirs = Get-ChildItem 'C:\Windows\WinSxS' -Directory -Filter '*userexperience-aix*' -ErrorAction SilentlyContinue
    if (-not $aixDirs) { return $true }
    $osBuild  = [System.Environment]::OSVersion.Version.Build
    $required = if ($osBuild -ge 26200) { 'snapshot_support.dll' } else { 'zlib1.dll' }
    foreach ($dir in $aixDirs) {
        if (-not (Test-Path (Join-Path $dir.FullName $required))) { return $true }
    }
    return $false
}

try {
    $pkgRoot  = 'C:\Windows\servicing\Packages'
    $aixFiles = Get-ChildItem -Path $pkgRoot -Filter '*UserExperience-AIX*' -ErrorAction SilentlyContinue |
                Where-Object { $_.Extension -in '.mum', '.cat' }

    if (-not $aixFiles) {
        $skipped.Add("SKIP  No UserExperience-AIX package metadata found - already clean")
    } elseif (-not (Test-AixBroken)) {
        $skipped.Add("SKIP  UserExperience-AIX payload present - package properly installed, no action needed")
    } else {
        foreach ($file in $aixFiles) {
            try {
                # Files are owned by TrustedInstaller; take ownership before delete
                $null = takeown.exe /f "$($file.FullName)" /a 2>&1
                $null = icacls.exe "$($file.FullName)" /grant "BUILTIN\Administrators:F" /q 2>&1
                Remove-Item -Path $file.FullName -Force -ErrorAction Stop
                $fixed.Add("FIXED $($file.Name)")
            } catch {
                $errors.Add("FAILED $($file.Name) - $_")
            }
        }
    }
} catch {
    $errors.Add("ERROR: Failed to enumerate servicing packages - $_")
}

if ($errors.Count -gt 0) {
    Write-Output "Remediation completed with $($errors.Count) error(s). Fixed: $($fixed.Count), Skipped: $($skipped.Count)"
    $errors  | ForEach-Object { Write-Output "  $_" }
    $fixed   | ForEach-Object { Write-Output "  $_" }
    $skipped | ForEach-Object { Write-Output "  $_" }
    exit 1
}

if ($fixed.Count -eq 0) {
    Write-Output "Nothing to fix - all $($skipped.Count) item(s) already compliant"
} else {
    Write-Output "Remediation completed. Fixed: $($fixed.Count), Skipped: $($skipped.Count)"
}
$fixed   | ForEach-Object { Write-Output "  $_" }
$skipped | ForEach-Object { Write-Output "  $_" }
exit 0

WinRE driver: detection script Link to heading

Targets hardware where the OS storage controller requires a third-party driver. Checks whether WinRE is enabled, then verifies the driver is present inside the WinRE image. Assign via an Intune filter for affected hardware (e.g. Manufacturer -eq "Dell Inc.").

#Requires -Version 5.1
<#
.SYNOPSIS
    Wipe Readiness - WinRE Driver Detection
    Ensures Windows RE is enabled and detects missing Intel RST storage driver in WinRE.
    Without the driver, WinRE cannot enumerate NVMe/RAID partitions during push-button reset,
    producing 0x800703ed (ERROR_UNRECOGNIZED_VOLUME) and "No changes were made."

    Exit 0 = Compliant (no remediation needed)
    Exit 1 = Non-compliant (triggers remediation)

.NOTES
    Deploy via: Intune > Devices > Remediations > Detection script
    Run as:     SYSTEM
    Run 64-bit: Yes
    Schedule:   Every 24 hours
    Target:     Hardware with Intel RST / RAID storage controllers
    Ref:        https://patchmypc.com/blog/there-was-a-problem-resetting-your-pc-remote-wipe/
#>

$ErrorActionPreference = 'Stop'
$issues   = [System.Collections.Generic.List[string]]::new()
$mountDir = $null
$mounted  = $false

# Check 1 - WinRE is enabled
try {
    $reagentInfo = reagentc /info 2>&1 | Out-String
    if ($reagentInfo -notmatch 'Windows RE status:\s+Enabled') {
        $issues.Add("WINRE: Windows RE is not enabled - remote wipe cannot proceed")
        Write-Output "Non-compliant ($($issues.Count) issue(s)):"
        $issues | ForEach-Object { Write-Output "  $_" }
        exit 1
    }
} catch {
    $issues.Add("ERROR: reagentc /info failed - $_")
    Write-Output "Non-compliant ($($issues.Count) issue(s)):"
    $issues | ForEach-Object { Write-Output "  $_" }
    exit 1
}

# Check 2 - Intel RST driver present on live OS
$rstDriver = $null
try {
    $rstDriver = Get-WindowsDriver -Online -ErrorAction Stop |
                 Where-Object { $_.OriginalFileName -match 'iastor' } |
                 Select-Object -First 1
} catch {
    $issues.Add("ERROR: Could not enumerate OS drivers - $_")
}

if (-not $rstDriver) {
    Write-Output "Compliant - Intel RST driver not present on OS"
    exit 0
}

# Check 3 - Intel RST driver present inside WinRE image
try {
    $reagentInfo  = reagentc /info 2>&1 | Out-String
    $locationLine = ($reagentInfo -split "`n") |
                    Where-Object { $_ -match 'Windows RE location' } |
                    Select-Object -First 1

    if (-not $locationLine) {
        $issues.Add("ERROR: Could not parse WinRE location from reagentc /info")
    } else {
        $winrePath = ($locationLine -replace '.*Windows RE location:\s*', '').Trim()
        $wimPath   = "$winrePath\winre.wim"

        $mountDir = Join-Path $env:TEMP 'WinREMount'
        New-Item -ItemType Directory -Path $mountDir -Force | Out-Null
        Mount-WindowsImage -ImagePath $wimPath -Index 1 -Path $mountDir -ErrorAction Stop | Out-Null
        $mounted = $true

        $winreDrivers = Get-WindowsDriver -Path $mountDir -ErrorAction Stop
        $hasRST       = $winreDrivers | Where-Object { $_.OriginalFileName -match 'iastor' }

        if (-not $hasRST) {
            $driverName = Split-Path $rstDriver.OriginalFileName -Leaf
            $issues.Add("DRIVER: $driverName present on OS but missing from WinRE - wipe fails with 0x800703ed")
        }
    }
} catch {
    $issues.Add("ERROR: WinRE driver check failed - $_")
} finally {
    if ($mounted) {
        Dismount-WindowsImage -Path $mountDir -Discard -ErrorAction SilentlyContinue | Out-Null
    }
    if ($mountDir -and (Test-Path $mountDir)) {
        Remove-Item -Path $mountDir -Force -Recurse -ErrorAction SilentlyContinue
    }
}

if ($issues.Count -eq 0) {
    Write-Output "Compliant"
    exit 0
}

Write-Output "Non-compliant ($($issues.Count) issue(s)):"
$issues | ForEach-Object { Write-Output "  $_" }
exit 1

WinRE driver: remediation script Link to heading

Enables WinRE if disabled, then injects the Intel RST driver from the live OS into the WinRE image. The WIM path is resolved from reagentc /info output and DISM mounts it directly using the \\?\GLOBALROOT\ path. Idempotent: skips injection if the driver is already present.

#Requires -Version 5.1
<#
.SYNOPSIS
    Wipe Readiness - WinRE Driver Remediation
    Step 1: Enable Windows RE if disabled (always - required for remote wipe).
    Step 2: Inject Intel RST storage driver from the live OS into the WinRE image
            (only if Intel RST is present on the OS).

.NOTES
    Deploy via: Intune > Devices > Remediations > Remediation script
    Run as:     SYSTEM
    Run 64-bit: Yes
    Target:     Hardware with Intel RST / RAID storage controllers
    Ref:        https://patchmypc.com/blog/there-was-a-problem-resetting-your-pc-remote-wipe/
#>

$ErrorActionPreference = 'Stop'
$fixed    = [System.Collections.Generic.List[string]]::new()
$skipped  = [System.Collections.Generic.List[string]]::new()
$errors   = [System.Collections.Generic.List[string]]::new()
$mountDir = $null
$mounted  = $false

# Step 1 - Enable WinRE if disabled (unconditional)
try {
    $reagentInfo = reagentc /info 2>&1 | Out-String
    if ($reagentInfo -notmatch 'Windows RE status:\s+Enabled') {
        reagentc /enable 2>&1 | Out-Null
        $reagentInfo2 = reagentc /info 2>&1 | Out-String
        if ($reagentInfo2 -notmatch 'Windows RE status:\s+Enabled') {
            $errors.Add("ERROR: WinRE could not be enabled - reagentc /enable failed. WIM may be missing or recovery partition damaged.")
        } else {
            $fixed.Add("FIXED Enabled Windows RE via reagentc /enable")
        }
    }
} catch {
    $errors.Add("ERROR: Failed to check or enable WinRE - $_")
}

# Step 2 - Inject RST driver into WinRE (only if RST present on OS)
if ($errors.Count -eq 0) {
    $rstDriver = $null
    try {
        $rstDriver = Get-WindowsDriver -Online -ErrorAction Stop |
                     Where-Object { $_.OriginalFileName -match 'iastor' } |
                     Select-Object -First 1
    } catch {
        $errors.Add("ERROR: Could not enumerate OS drivers - $_")
    }

    if (-not $rstDriver) {
        $skipped.Add("SKIP  Intel RST driver not present on OS - nothing to inject")
    } elseif ($errors.Count -eq 0) {
        $infPath = $rstDriver.OriginalFileName -replace [regex]::Escape('\SystemRoot'), $env:SystemRoot

        if (-not (Test-Path $infPath)) {
            $errors.Add("FAILED Could not locate RST driver INF at $infPath")
        } else {
            try {
                $reagentInfo  = reagentc /info 2>&1 | Out-String
                $locationLine = ($reagentInfo -split "`n") |
                                Where-Object { $_ -match 'Windows RE location' } |
                                Select-Object -First 1

                if (-not $locationLine) {
                    $errors.Add("ERROR: Could not parse WinRE location from reagentc /info")
                } else {
                    $winrePath = ($locationLine -replace '.*Windows RE location:\s*', '').Trim()
                    $wimPath   = "$winrePath\winre.wim"

                    $mountDir = Join-Path $env:TEMP 'WinREMount'
                    New-Item -ItemType Directory -Path $mountDir -Force | Out-Null
                    Mount-WindowsImage -ImagePath $wimPath -Index 1 -Path $mountDir -ErrorAction Stop | Out-Null
                    $mounted = $true

                    $winreDrivers   = Get-WindowsDriver -Path $mountDir -ErrorAction Stop
                    $alreadyPresent = $winreDrivers | Where-Object { $_.OriginalFileName -match 'iastor' }

                    if ($alreadyPresent) {
                        $skipped.Add("SKIP  Intel RST driver already present in WinRE")
                    } else {
                        Add-WindowsDriver -Path $mountDir -Driver $infPath -ErrorAction Stop | Out-Null
                        $driverName = Split-Path $infPath -Leaf
                        $fixed.Add("FIXED Injected $driverName into WinRE")
                    }
                }
            } catch {
                $errors.Add("ERROR: $($_.Exception.Message)")
            } finally {
                if ($mounted) {
                    try {
                        if ($errors.Count -eq 0) {
                            Dismount-WindowsImage -Path $mountDir -Save -ErrorAction Stop | Out-Null
                        } else {
                            Dismount-WindowsImage -Path $mountDir -Discard -ErrorAction SilentlyContinue | Out-Null
                        }
                    } catch {
                        $errors.Add("ERROR: Failed to dismount WinRE image - $_")
                    }
                }
                if ($mountDir -and (Test-Path $mountDir)) {
                    Remove-Item -Path $mountDir -Force -Recurse -ErrorAction SilentlyContinue
                }
            }
        }
    }
}

if ($errors.Count -gt 0) {
    Write-Output "Remediation completed with $($errors.Count) error(s). Fixed: $($fixed.Count), Skipped: $($skipped.Count)"
    $errors  | ForEach-Object { Write-Output "  $_" }
    $fixed   | ForEach-Object { Write-Output "  $_" }
    $skipped | ForEach-Object { Write-Output "  $_" }
    exit 1
}

if ($fixed.Count -eq 0) {
    Write-Output "Nothing to fix - all $($skipped.Count) item(s) already compliant"
} else {
    Write-Output "Remediation completed. Fixed: $($fixed.Count), Skipped: $($skipped.Count)"
}
$fixed   | ForEach-Object { Write-Output "  $_" }
$skipped | ForEach-Object { Write-Output "  $_" }
exit 0

What gets removed and what does not Link to heading

Removing the UserExperience-AIX metadata files is a targeted workaround, not a broad servicing change. The package binaries remain in WinSxS. Only the CBS registration reference is removed. Windows Recall will not initialise on devices where it was already disabled by policy, which is the case on all managed enterprise endpoints. SFC /scannow will report integrity violations after remediation because the metadata no longer matches the WinSxS content: this is expected and cosmetic. Do not use SFC output as an alerting signal after applying the fix.

If a future Cumulative Update reintroduces the broken package, the daily detection script will catch it and the remediation will run again. The loop is self-healing. When Microsoft ships a corrected version of the package, the detection script will exit compliant and no remediation runs.