85°F Knoxville
Mon–Fri 9–5 ETAnswered by a human
Automation · RESOLVED

Fixing Windows Update Stuck in Reboot Loop: Nuclear Reset

HR laptop cycling 'Undoing changes' for 6 hours. 24H2 update failing at 47%. Benefits deadline in 3 hours. Standard fixes failed.

Incident

The ticket came in as "laptop keeps restarting — I can't work." HR director had left her laptop on overnight to "let it update" and came back to find it mid-rollback. We connected remotely and watched it cycle through the same pattern: boot, attempt update, fail at 47%, "We couldn't complete the updates. Undoing changes," reboot. Rinse and repeat. Six hours of this by the time we got on.

The bad part: she was three hours away from the annual benefits enrollment cutoff, and this was the laptop she needed to finalize enrollments on. Machine was unusable during the only window that mattered.

Problem identification

  • update failure: Windows 11 24H2 feature update failing consistently at 47%
  • error code: event log showing 0x80070002 — "the system cannot find the file specified"
  • disk space: 89 GB free — not a space issue
  • SoftwareDistribution: 12 GB folder full of corrupted download fragments

The user had tried to "help" by force-restarting during a previous update attempt two weeks ago. That interrupted update left behind corrupted component store entries, and they'd been poisoning every subsequent update attempt since.

What we tried first

We had a 3-hour window before the benefits system locked out. Worked through the standard escalation path:

  • Windows Update Troubleshooter — said "Windows Update components must be repaired," then did nothing
  • DISM /RestoreHealth — failed with 0x800f081f, couldn't find source files because the component store itself was corrupted
  • SFC /scannow — found corrupt files but couldn't fix them, needed DISM to work first
  • delete SoftwareDistribution — service wouldn't stop, "access denied" even as admin
  • safe-mode cleanup — deleted the folder, but the corruption had already spread to catroot2 and the BITS queue

The corruption had cascaded. Every partial fix attempt was making it worse. We needed to reset everything at once — services, caches, DLL registrations, security descriptors, network stack. Nuclear option.

Resolution

Booted to Safe Mode with Networking, connected via RMM, and ran this script. Same procedure Microsoft support would walk through over two hours on the phone — automated and executed in 90 seconds.

windows_update_reset.ps1View on GitHub →
$ErrorActionPreference = 'Stop'

<#
██╗     ██╗███╗   ███╗███████╗██╗  ██╗ █████╗ ██╗    ██╗██╗  ██╗
██║     ██║████╗ ████║██╔════╝██║  ██║██╔══██╗██║    ██║██║ ██╔╝
██║     ██║██╔████╔██║█████╗  ███████║███████║██║ █╗ ██║█████╔╝
██║     ██║██║╚██╔╝██║██╔══╝  ██╔══██║██╔══██║██║███╗██║██╔═██╗
███████╗██║██║ ╚═╝ ██║███████╗██║  ██║██║  ██║╚███╔███╔╝██║  ██╗
╚══════╝╚═╝╚═╝     ╚═╝╚══════╝╚═╝  ╚═╝╚═╝  ╚═╝ ╚══╝╚══╝ ╚═╝  ╚═╝
================================================================================
 SCRIPT   : Windows Update Reset                                         v1.0.2
 AUTHOR   : Limehawk.io
 DATE      : January 2026
 USAGE    : .\windows_update_reset.ps1
================================================================================
 FILE     : windows_update_reset.ps1
DESCRIPTION : Resets Windows Update components and clears cache
--------------------------------------------------------------------------------
 README
--------------------------------------------------------------------------------
 PURPOSE

 Completely resets Windows Update components by stopping services, clearing
 caches, resetting security descriptors, re-registering DLLs, and restarting
 services. Fixes most Windows Update problems.

 DATA SOURCES & PRIORITY

 1) Hardcoded values (defined within the script body)
 2) Windows Update services and components

 REQUIRED INPUTS

 - RebootAfterReset : Whether to reboot after reset (default: $false)

 SETTINGS

 - Stops: BITS, wuauserv, appidsvc, cryptsvc
 - Clears: SoftwareDistribution, catroot2, QMGR data
 - Resets: BITS and wuauserv security descriptors
 - Registers: 30+ Windows Update related DLLs
 - Resets: Winsock

 BEHAVIOR

 1. Stops all Windows Update related services
 2. Flushes DNS cache
 3. Clears BITS download queue data
 4. Renames SoftwareDistribution and catroot2 folders
 5. Resets Windows Update policies in registry
 6. Resets BITS and wuauserv security descriptors
 7. Re-registers all Windows Update DLLs
 8. Resets Winsock
 9. Restarts all services
 10. Optionally reboots system

 PREREQUISITES

 - Windows 10/11
 - Admin privileges required
 - Internet connectivity for updates after reset

 SECURITY NOTES

 - No secrets in logs
 - Modifies system registry and service configurations
 - Creates .bak folders for recovery

 EXIT CODES

 - 0: Success
 - 1: Failure

 EXAMPLE RUN

 [INFO] INPUT VALIDATION
 ==============================================================
 Reboot After Reset : False

 [RUN] STOPPING SERVICES
 ==============================================================
 Stopping BITS...
 Stopping wuauserv...
 Stopping appidsvc...
 Stopping cryptsvc...

 [RUN] CLEARING CACHES
 ==============================================================
 Flushing DNS...
 Clearing QMGR data...
 Renaming SoftwareDistribution...
 Renaming catroot2...

 [RUN] RESETTING COMPONENTS
 ==============================================================
 Resetting security descriptors...
 Re-registering DLLs...
 Resetting Winsock...

 [RUN] STARTING SERVICES
 ==============================================================
 Starting BITS...
 Starting wuauserv...
 Starting appidsvc...
 Starting cryptsvc...

 [OK] RESULT
 ==============================================================
 Status : Success

 [OK] SCRIPT COMPLETED
 ==============================================================
--------------------------------------------------------------------------------
 CHANGELOG
--------------------------------------------------------------------------------
 2026-01-19 v1.0.2 Updated to two-line ASCII console output style
 2025-12-23 v1.0.1 Updated to Limehawk Script Framework
 2025-11-29 v1.0.0 Initial Style A implementation
================================================================================
#>

Set-StrictMode -Version Latest

# ==== STATE ====
$errorOccurred = $false
$errorText = ""

# ==== HARDCODED INPUTS ====
$RebootAfterReset = $false

# ==== ADMIN CHECK ====
$isAdmin = ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")
if (-not $isAdmin) {
    Write-Host ""
    Write-Host "[ERROR] ADMIN PRIVILEGES REQUIRED"
    Write-Host "=============================================================="
    Write-Host "Script requires admin privileges."
    Write-Host "Please relaunch as Administrator."
    exit 1
}

# ==== RUNTIME OUTPUT ====
Write-Host ""
Write-Host "[INFO] INPUT VALIDATION"
Write-Host "=============================================================="
Write-Host "Reboot After Reset : $RebootAfterReset"

# ==== STOP SERVICES ====
Write-Host ""
Write-Host "[RUN] STOPPING SERVICES"
Write-Host "=============================================================="

$services = @('bits', 'wuauserv', 'appidsvc', 'cryptsvc')
foreach ($svc in $services) {
    Write-Host "Stopping $svc..."
    Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue
    $retries = 0
    while ((Get-Service -Name $svc -ErrorAction SilentlyContinue).Status -ne 'Stopped' -and $retries -lt 3) {
        Start-Sleep -Seconds 2
        Stop-Service -Name $svc -Force -ErrorAction SilentlyContinue
        $retries++
    }
}

# ==== CLEAR CACHES ====
Write-Host ""
Write-Host "[RUN] CLEARING CACHES"
Write-Host "=============================================================="

try {
    Write-Host "Flushing DNS..."
    ipconfig /flushdns | Out-Null

    Write-Host "Clearing QMGR data..."
    Remove-Item -Path "$env:ALLUSERSPROFILE\Application Data\Microsoft\Network\Downloader\qmgr*.dat" -Force -ErrorAction SilentlyContinue
    Remove-Item -Path "$env:ALLUSERSPROFILE\Microsoft\Network\Downloader\qmgr*.dat" -Force -ErrorAction SilentlyContinue

    Write-Host "Clearing Windows Update logs..."
    Remove-Item -Path "$env:SYSTEMROOT\Logs\WindowsUpdate\*" -Recurse -Force -ErrorAction SilentlyContinue

    # Rename SoftwareDistribution
    Write-Host "Renaming SoftwareDistribution..."
    $sdPath = "$env:SYSTEMROOT\SoftwareDistribution"
    $sdBackup = "$env:SYSTEMROOT\SoftwareDistribution.bak"
    if (Test-Path $sdBackup) {
        Remove-Item -Path $sdBackup -Recurse -Force -ErrorAction SilentlyContinue
    }
    if (Test-Path $sdPath) {
        Rename-Item -Path $sdPath -NewName "SoftwareDistribution.bak" -Force -ErrorAction Stop
    }

    # Rename catroot2
    Write-Host "Renaming catroot2..."
    $crPath = "$env:SYSTEMROOT\System32\catroot2"
    $crBackup = "$env:SYSTEMROOT\System32\catroot2.bak"
    if (Test-Path $crBackup) {
        Remove-Item -Path $crBackup -Recurse -Force -ErrorAction SilentlyContinue
    }
    if (Test-Path $crPath) {
        Rename-Item -Path $crPath -NewName "catroot2.bak" -Force -ErrorAction Stop
    }

} catch {
    Write-Host "Warning: Some cache operations failed: $($_.Exception.Message)"
}

# ==== RESET COMPONENTS ====
Write-Host ""
Write-Host "[RUN] RESETTING COMPONENTS"
Write-Host "=============================================================="

try {
    # Reset Windows Update policies
    Write-Host "Resetting Windows Update policies..."
    $regPaths = @(
        "HKCU:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate",
        "HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\WindowsUpdate",
        "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate",
        "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\WindowsUpdate"
    )
    foreach ($path in $regPaths) {
        if (Test-Path $path) {
            Remove-Item -Path $path -Recurse -Force -ErrorAction SilentlyContinue
        }
    }

    # Reset security descriptors
    Write-Host "Resetting service security descriptors..."
    sc.exe sdset bits "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)" | Out-Null
    sc.exe sdset wuauserv "D:(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPWPDTLOCRRC;;;PU)" | Out-Null

    # Re-register DLLs
    Write-Host "Re-registering Windows Update DLLs..."
    $dlls = @(
        "atl.dll", "urlmon.dll", "mshtml.dll", "shdocvw.dll", "browseui.dll",
        "jscript.dll", "vbscript.dll", "scrrun.dll", "msxml.dll", "msxml3.dll",
        "msxml6.dll", "actxprxy.dll", "softpub.dll", "wintrust.dll", "dssenh.dll",
        "rsaenh.dll", "gpkcsp.dll", "sccbase.dll", "slbcsp.dll", "cryptdlg.dll",
        "oleaut32.dll", "ole32.dll", "shell32.dll", "initpki.dll", "wuapi.dll",
        "wuaueng.dll", "wuaueng1.dll", "wucltui.dll", "wups.dll", "wups2.dll",
        "wuweb.dll", "qmgr.dll", "qmgrprxy.dll", "wucltux.dll", "muweb.dll",
        "wuwebv.dll", "wudriver.dll"
    )
    $system32 = "$env:SYSTEMROOT\System32"
    foreach ($dll in $dlls) {
        $dllPath = Join-Path $system32 $dll
        if (Test-Path $dllPath) {
            regsvr32.exe /s $dllPath 2>$null
        }
    }

    # Reset Winsock
    Write-Host "Resetting Winsock..."
    netsh winsock reset | Out-Null
    netsh winsock reset proxy | Out-Null

    # Set services to auto start
    Write-Host "Configuring service startup types..."
    sc.exe config wuauserv start= auto | Out-Null
    sc.exe config bits start= auto | Out-Null
    sc.exe config DcomLaunch start= auto | Out-Null

} catch {
    $errorOccurred = $true
    $errorText = $_.Exception.Message
}

# ==== START SERVICES ====
Write-Host ""
Write-Host "[RUN] STARTING SERVICES"
Write-Host "=============================================================="

foreach ($svc in $services) {
    Write-Host "Starting $svc..."
    Start-Service -Name $svc -ErrorAction SilentlyContinue
}

if ($errorOccurred) {
    Write-Host ""
    Write-Host "[WARN] ERRORS OCCURRED"
    Write-Host "=============================================================="
    Write-Host $errorText
}

if ($errorOccurred) {
    Write-Host ""
    Write-Host "[WARN] RESULT"
    Write-Host "=============================================================="
    Write-Host "Status : Partial Success (some operations failed)"
} else {
    Write-Host ""
    Write-Host "[OK] RESULT"
    Write-Host "=============================================================="
    Write-Host "Status : Success"
}

Write-Host ""
Write-Host "[INFO] FINAL STATUS"
Write-Host "=============================================================="
Write-Host "Windows Update components have been reset."
Write-Host "A reboot is recommended to complete the process."

if ($RebootAfterReset) {
    Write-Host "Initiating reboot in 60 seconds..."
    shutdown /g /f /t 60 /c "Windows Update reset complete. Rebooting..."
}

if ($errorOccurred) {
    Write-Host ""
    Write-Host "[WARN] SCRIPT COMPLETED"
    Write-Host "=============================================================="
} else {
    Write-Host ""
    Write-Host "[OK] SCRIPT COMPLETED"
    Write-Host "=============================================================="
}

if ($errorOccurred) {
    exit 1
} else {
    exit 0
}

Why this works

Windows Update is a fragile stack of interdependent components. When corruption spreads between them, targeted fixes fail because they can't account for the cascading damage. This script resets everything at once.

What each phase does:

  1. stop services: BITS, wuauserv, appidsvc, cryptsvc — releases locks on corrupted files
  2. flush DNS: clears stale entries pointing at dead Microsoft update servers
  3. clear QMGR data: purges corrupted BITS download queue entries
  4. rename SoftwareDistribution: forces Windows to rebuild the entire download cache fresh
  5. rename catroot2: resets the signature verification database, fixes trust chain issues
  6. reset security descriptors: restores default permissions corrupted by malware or bad software
  7. re-register DLLs: fixes 15 critical COM registrations that cause cryptic update errors
  8. reset Winsock: clears network catalog corruption blocking downloads at the TCP/IP level

Why Safe Mode: ensures no third-party software is holding locks on Windows Update components. Also prevents the update service from automatically restarting mid-reset, which would undo half the work.

Outcome

Script completed in 87 seconds. Rebooted to normal mode, waited 10 minutes for Windows to rebuild its update database, then manually triggered Windows Update. The 24H2 feature update downloaded fresh and installed successfully on the first attempt. HR director was back online with 47 minutes to spare before the enrollment deadline.

  • time to fix: 2 hours 13 min (including the failed earlier attempts)
  • script runtime: 87 seconds
  • deadline met: yes, 47 minutes to spare

Key takeaways