How I Broke Windows Update by Freeing Up Disk Space

A Veeam backup server on Windows Server 2025 started rebooting itself after a PowerShell upgrade failed with a 'please insert the disk' prompt — and the root cause turned out to be a directory I had deleted, weeks earlier, to reclaim space.

How I Broke Windows Update by Freeing Up Disk Space

Every self-inflicted outage starts with a small act of tidiness. Mine started with me looking at a Windows Server 2025 box, noticing that C:\Windows\Installer had quietly swollen to several gigabytes of files I did not recognise, and deciding — reasonably, I thought — to clear it out and reclaim the space. The server thanked me by working perfectly for weeks. Then Windows Update tried to upgrade PowerShell, and the bill came due.

The machine in question was not a toy. It was running Veeam Backup & Replication — which is to say it was the one box in the lab whose entire job is to be trustworthy, the safety net under everything else. And it had started rebooting itself, unprompted, shortly after an update attempt. The thing that protects me from my own mistakes had been taken down by exactly one of them.

C:\Windows\Installer is not the junk it looks like. It is the receipt Windows keeps for every MSI you have ever installed, and Windows Installer cannot repair, upgrade or remove what it has no receipt for.

The symptom that made no sense

The first symptom was the alarming one: a production Windows Server 2025 VM restarting on its own. The second symptom is what turned a vague worry into a specific problem. Instead of a normal Windows Update failure — a hex error code, a line in the update history, something I could search — the installer kept stopping to ask me for something absurd:

PowerShell 7-x64 is on a CD-ROM or other removable disk. Insert the disk and click OK.

There was no disk. There never had been a disk. PowerShell had arrived on that server the way everything does now — as a download, years ago. The update could not complete, the server was left in an unstable state, and the existing PowerShell still ran but reported versions that did not agree with each other. A prompt for media that has never existed is the kind of error that makes you doubt the machine before you doubt yourself, which, as it turned out, was exactly the wrong way round.

What the packages were telling me, and why I didn’t believe them

When three tools disagree about something as simple as “which version is installed”, the disagreement is the clue. So I asked all three.

Get-Package cheerfully reported two versions living side by side, 7.4.13 and 7.4.14, as if the upgrade had half-happened:

Get-Package -Name "PowerShell*"
# reported both 7.4.13 and 7.4.14

The actual executable disagreed. Get-Command still pointed at the old binary, untouched:

Get-Command pwsh
# C:\Program Files\PowerShell\7\pwsh.exe  →  7.4.13

And the MSI database — the thing Windows Installer itself trusts — only believed in one of them:

Get-WmiObject Win32_Product |
  Where-Object { $_.Name -like "*PowerShell*" } |
  Select-Object Name, Version, IdentifyingNumber

# PowerShell 7-x64
# 7.4.13
# {BD168355-B595-4C5B-B9ED-05C4714A01A5}

(A small aside, because knowing your tools’ sharp edges is half the job: Win32_Product is a slow and slightly dangerous way to ask this question — enumerating it makes Windows Installer run a consistency check against every installed MSI as it goes — but it reads from the same place the upgrade does, which is precisely why I wanted its answer here.)

Three tools, three stories. The reconciliation was simple once I stopped arguing with it: the 7.4.14 upgrade had never actually landed. The package provider had noticed a newer thing being attempted and listed it; the filesystem and the MSI database both still belonged entirely to 7.4.13. The upgrade was stuck halfway through a door it could not open, and the whole estate was being held hostage by whatever was jamming that door.

The cause, which was me

Here is the part I would bolt to the front of the runbook. The upgrade could not complete because Windows Installer could not get at the original 7.4.13 installation to remove and replace it — and it could not get at it because I had deleted the cache that held it.

C:\Windows\Installer is not scratch space. It is where Windows Installer stashes a copy of the MSI (and any patches) for every product on the machine, so that later — when you repair, upgrade or uninstall something — it has a known-good source to work from without asking you for anything. A PowerShell point upgrade is, under the bonnet, a major upgrade: remove the old product, install the new one. The “remove the old product” step needed the cached 7.4.13 MSI. I had thrown it away to save a few gigabytes, so Windows Installer did the only thing it knows how to do when its source is missing — it fell back to asking for the original media. That is the “please insert the disk” prompt: not a bug, just Installer politely requesting the receipt I had binned. And every time the update retried and failed, it dragged the server through another reboot cycle.

I had assumed C:\Windows\Installer was Microsoft’s mess to clean up. It is the opposite — it is Microsoft’s safety copy, and deleting it is one of those housekeeping moves that does nothing bad at all until the precise moment something needs to be repaired. This is the home lab’s favourite kind of lesson: the consequence arrives weeks after the decision, with no obvious thread connecting them, so you get to learn causation the hard way. Obvious in hindsight. Invisible at the time.

The fix: stop guessing and repair the MSI

The temptation at this point is to start randomly repairing things — re-run the update, click retry, point the prompt at any MSI you can find and hope. That is how a one-evening problem becomes a three-evening one. The discipline is to fix the actual thing that is broken: give Windows Installer back a valid source for the product it is trying to upgrade.

So I worked from the registration I had already confirmed — that product code above, {BD168355-…} — and went and got a clean, verified copy of the installer rather than trusting whatever a browser handed me. Winget will do exactly this, and because PowerShell’s MSI is built with WiX I could ask it for the installer directly:

winget download --id Microsoft.PowerShell --installer-type wix

That sidesteps the whole circus of browser downloads being intercepted by security software, and it checks the installer hash for me so I am not guessing whether I have the right, untampered package. Then I ran it by hand through msiexec, letting Windows Installer do the upgrade properly against the existing registration:

msiexec /i "<path to the downloaded MSI>"

That single step did everything I needed. Installer used the new MSI to complete the major upgrade, re-cached the package back into C:\Windows\Installer — quietly rebuilding the receipt I had deleted — and finished cleanly. PowerShell came up on the new version, Windows Update stopped asking for a disk that does not exist, and the server stopped throwing itself on the floor. The safety net was back under the trapeze.

What I’d tell past-me

Three things, because the mistakes taught me more than the fix did.

C:\Windows\Installer is not yours to tidy. It looks like wasted space and it is anything but. If a server is genuinely tight on disk, the answer is more disk, a bigger volume, or DISM/component-store cleanup — never reaching into the Installer cache with a delete key. The space you reclaim is a loan against the next repair or upgrade, and the interest is brutal.

Don’t trust a single package view. Get-Package, Get-WmiObject Win32_Product (or its modern Get-CimInstance form), and pwsh -Version can and will disagree, because they read different sources of truth. When they diverge, the divergence is the diagnosis — and the view that matters for an upgrade is the one Windows Installer itself trusts, which is the MSI database, not the package provider.

winget download is a recovery tool, not just an install one. When a browser download is failing, or being quietly intercepted, or you simply cannot be sure you have the right untampered file, winget download hands you a verified installer and removes the guesswork about provenance entirely. It earned its place in my toolkit on this exact problem.

And underneath all three, the meta-lesson that keeps this site honest: I got to learn all of it at the price of one evening on my own hardware instead of discovering it on a client’s production backup server at 3am. That is the entire argument for the home lab as a learning platform — you make the expensive mistakes where the only thing on fire is your weekend. The fix went straight into my second brain the moment it worked, because the version of me who meets this again in two years will have completely forgotten the product code, the WiX flag, and the housekeeping that caused it.

Useful commands

For future-me, and anyone else staring at a phantom CD-ROM prompt:

# What does each source actually believe is installed?
Get-Package -Name "PowerShell*"
Get-WmiObject Win32_Product | Where-Object { $_.Name -like "*PowerShell*" }
pwsh -Version

# Which binary is actually on PATH?
Get-Command pwsh

# Get a clean, hash-verified installer (the WiX-built MSI)
winget download --id Microsoft.PowerShell --installer-type wix

# Let Windows Installer repair/upgrade against the existing registration
msiexec /i "<path to the downloaded MSI>"

Closing thought

The unexpected restarts were the symptom that made me look. The lesson underneath was older and quieter: a backup server I had subtly broken, weeks earlier, with an act of tidiness I never connected to the failure. When Windows Installer tells you a product is “on a CD-ROM or other removable disk”, it is almost never talking about the application. It is telling you its own bookkeeping is no longer consistent — that the receipt is missing — and the only real fix is to give it a valid source and let it put its records back in order.

The irony is not lost on me that the machine I undermined was the one whose entire purpose is to recover from disasters, and that I did it not with a risky change but with cleanup — the kind of “responsible” maintenance that feels like the opposite of a risk. That, more than the msiexec incantation, is what I took away. The dangerous changes announce themselves. It is the housekeeping you should keep an eye on. This one fits neatly alongside backups, the boring discipline: the boring, well-meant, routine work is exactly where the quiet failures hide.