Windows Installer caches a product’s MSI database in the hidden directory %WINDIR%\Installer, which is how users can uninstall without having to put the CD back in. (In MSI Automation, Installer.ProductInfo with constant LocalPackage will tell you where the cached package is; ditto MsiGetProductInfo with INSTALLPROPERTY_LOCALPACKAGE.) By default, maintenance operations such as repairs use this cached database, which is why testing is easier if you uninstall an old version before installing a new one: your changes in a new build will ordinarily not be available because they’re not in the cached database.
Trouble arises if you have a custom action that crashes during uninstallation: you can’t just make a new build without the error and run it, since maintenance mode will continue to use the cached MSI database.
(A common beginner mistake is not realizing that the same sequences are used for both installation and uninstallation, and therefore a custom action with no condition will run for installation, repairs, modifications, and uninstallation. Not that you asked, but the condition Not Installed succeeds for a first-time installation, and REMOVE="ALL" will work in the Execute sequence after InstallValidate to detect a complete uninstallation. Mind the capitalization and quotation marks.)
To re-cache the MSI database so a subsequent uninstallation will work, deploy the new build (with the error removed), setting the REINSTALLMODE property to include the letter v, as in:
msiexec /i product.msi REINSTALLMODE=voums REINSTALL=ALL
This is how minor upgrades are deployed, too, of course.
Occasionally you’ll hear suggestions to use the Windows Installer Cleanup Utility or msizap, which is overdoing things, or to find and edit the cached MSI database in the hidden Installer directory, which is just crazy.