A cumulative update downloads cleanly, sits at 100 percent, and then fails with a trust error. Or Windows Update refuses to scan at all. Within about ninety seconds somebody in the thread says: stop Cryptographic Services, delete catroot2, restart. It gets repeated so often that it has stopped sounding like advice and started sounding like a fact.
It is not a fact. It is a mangled version of step 4 of a ten-step Microsoft procedure, and Microsoft's version of that step does not contain the word delete.
C:\Windows\System32\catroot holds the signed catalog files themselves. catroot2 holds only the database that indexes them. On the lab device that is 7,778 catalogs and 208.8 MB in catroot against a 127 MB catdb in catroot2 - so catroot2 is the derived copy, and catroot is the irreplaceable original. Microsoft's documented reset sequence renames catroot2 to catroot2.bak, gates that step behind "only if everything else failed", and never touches catroot at all. Deleting catroot2 also only addresses one of at least four distinct trust-failure classes, and it destroys the evidence you need to tell them apart. Diagnose first: decode the error, ask the file itself with Get-AuthenticodeSignature, build the chain in isolation, and read the root-CTL sync timestamps out of the registry.
The problem: the package downloads, then refuses to install
This class of failure has two faces, and they look nothing like each other from the console.
The first face is an install-time trust failure. The package transfers fine, so BITS and Delivery Optimization are clearly healthy, and then the handler rejects it. You get codes in the 0x800B or 0x8009 ranges - 0x800B0100, 0x800B0109, 0x80096010 - which are the WinTrust and CryptoAPI facilities, not the Windows Update facility at all.
The second face is a scan that never gets off the ground. The client cannot validate the metadata it just pulled, so it never produces a list of applicable updates. On the lab device the Windows Update client log carries exactly one Event 25 - "Windows Update failed to check for updates with error 0x80240438" - against 344 Event 26 entries and 55 Event 41 entries, which is the shape of a device that scans and downloads normally.
What both faces share is that the failure happens inside signature verification, and signature verification is not part of Windows Update. It is a separate subsystem - WinTrust, CryptoAPI, the catalog database and the certificate stores - that Windows Update merely calls into. That is the single most useful thing to internalise here, because it means every fix aimed at Windows Update components is aimed at the wrong subsystem.
It also means this is a different animal from the payload-missing class. If your error is 0x800f081f, Microsoft documents that as CBS_E_SOURCE_MISSING with the greppable string "ResolveSource() unsuccessful" - the servicing stack could not find bits it needed, which is a component-store problem, not a trust problem. A sibling post in this series covers that one. Do not treat the two as interchangeable just because both end in a failed install.
Ren %Systemroot%\System32\catroot2 catroot2.bak - a rename, at step 4 of 10, with an explicit instruction to skip step 4 on your first attempt. Those are not the same operation, and the difference is the difference between a reversible change and an irreversible one.There is a worse variant. Because catroot and catroot2 differ by one character, the instruction gets generalised in retelling to "delete the catroot folders". On the lab device that would remove 7,778 catalog files totalling 208.8 MB, the oldest of which is dated 14 May 2014. Nothing currently installed on that machine is going to put a 2014 catalog back. The catalog database rebuilds itself; the catalogs do not.
Why it happens: catalogs, the catalog database, and the chain to a trusted root
Start with what a catalog actually is, because almost every wrong conclusion in this area comes from not knowing. Microsoft's driver documentation is precise: a catalog file "contains a collection of cryptographic hashes, or thumbprints", and "each thumbprint corresponds to a file that is included in the collection".
So a catalog is a detached signature for a set of files. The individual files are not signed; the catalog is signed, and it vouches for their hashes. That is why a single byte changed in any covered file invalidates the whole thing, and why the same documentation says the system installs catalogs into CatRoot and that "catalog files shouldn't be added to or removed from that directory manually".
You can see this on any Windows binary. Ask PowerShell about wuaueng.dll and it reports SignatureType : Catalog, not Authenticode - the DLL carries no embedded signature of its own, and its trust comes entirely from a catalog sitting in catroot. Microsoft's own cmdlet documentation states the precedence rule outright: "if the file is both embedded signed and Windows catalog signed, the Windows catalog signature is used."
Read those two blocks against each other and the asymmetry is obvious. catroot2 contains two files per subtree: catdb and catdb.jfm. The .jfm extension is the giveaway - that is an ESE flush map, so catdb is a Jet/ESE database whose entire content is derived from the 7,778 files next door.
The service that owns that database is Cryptographic Services, and its own description tells you why it matters to patching. This is the string the service publishes on the lab device, not a paraphrase: it "provides three management services: Catalog Database Service, which confirms the signatures of Windows files and allows new programs to be installed; Protected Root Service, which adds and removes Trusted Root Certification Authority certificates from this computer; and Automatic Root Certificate Update Service, which retrieves root certificates from Windows Update".
Three jobs, one service. Catalog validation, root store writes, and root certificate updates. A single service failure therefore breaks package trust, root management and root refresh simultaneously - which is exactly why trust failures feel so much more catastrophic than they are.
Two details there are worth pinning. CryptSvc runs as NetworkService, not LocalSystem, which matters when you are chasing permission errors. And its only declared dependency is RpcSs, because the local catalog client reaches it over RPC - keep that in mind for the error decoding later.
The binaries that do the actual verifying are separate again, and worth naming because the documented repair scripts touch them.
| Binary | Where it lives | What it does (verified role and file description) |
|---|---|---|
wintrust.dll | C:\Windows\System32 (10.0.26100.8972) | File description "Microsoft Trust Verification APIs". Exports WinVerifyTrust, the function that decides whether a subject is trusted, plus the CryptCATAdmin* catalog APIs. This is the DLL that produces your 0x800B code. |
softpub.dll | C:\Windows\System32 (10.0.26100.9168) | "Softpub Forwarder DLL" - the Software Publisher trust provider surface. WINTRUST_ACTION_GENERIC_VERIFY_V2, the Authenticode policy action, is defined in Softpub.h. |
crypt32.dll | C:\Windows\System32 | "Crypto API32" - certificate stores and chain building. It is also the message resource file for the Microsoft-Windows-CAPI2 event provider, which is how you get readable chain diagnostics. |
cryptsvc.dll | C:\Windows\System32 | "Cryptographic Services" - the ServiceDll loaded into the svchost.exe -k NetworkService -p host. This is the process that owns catdb. |
mssign32.dll | C:\Windows\System32 | "Microsoft Trust Signing APIs" - the signing-side counterpart to wintrust. Present on clients; you will not normally call it. |
wuaueng.dll | C:\Windows\System32 (1509.2607.1012.0) | "Windows Update Agent" - the ServiceDll for wuauserv. Note the version: it is serviced separately from the OS build, and it is itself catalog-signed, which is why a broken catalog database can stop the agent from loading cleanly. |
initpki.dll | Does not exist on 26200.9168 | Listed in Microsoft's own reset procedure for regsvr32 re-registration. It is not on the disk. More on that in the fix section. |
Now the chain. A catalog is signed by a leaf certificate, which is issued by an intermediate, which chains to a root that must be present and trusted in the local machine store. Every hop is a place this can break, and each break has its own error code.
Three elements: leaf, intermediate, root. Break element 2 and you get 0x800B0109, CERT_E_UNTRUSTEDROOT, "a certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider". Break the chain earlier and you get 0x800B010A, CERT_E_CHAINING, "a certificate chain could not be built to a trusted root authority". Those are two different faults with two different fixes, and neither of them is a catroot2 fault.
The root store is kept current by the third of CryptSvc's three jobs. Microsoft documents the mechanism: clients fetch certificate trust lists from ctldl.windowsupdate.com over HTTP on TCP 80, pulling authrootstl.cab (the trusted CTL), disallowedcertstl.cab and disallowedcert.sst (the untrusted CTL and store), and individual <thumbprint>.crt files. Roughly 1.5 MB in total. This traffic is not the same as your Windows Update traffic, and it is routinely missed by proxy allow-lists that were written for *.windowsupdate.com download endpoints only.
CN=Microsoft Code Signing PCA 2011, which expired on 8 July 2026 - 46 days before this measurement. Every update on that device installs. The countersignature is doing its job.That gives you four failure classes to distinguish, and only one of them has anything to do with the catalog database.
| Class | Documented code and meaning | What is actually wrong |
|---|---|---|
| No signature found | 0x800B0100 TRUST_E_NOSIGNATURE - "no signature was present in the subject" | The catalog covering the file is missing, truncated or not indexed. This is the only class where the catalog store or its database is genuinely implicated. |
| Untrusted or unbuildable chain | 0x800B0109 CERT_E_UNTRUSTEDROOT; 0x800B010A CERT_E_CHAINING | A root is absent from the store, or an intermediate cannot be found. Root CTL sync failure, a stripped store, or a blocked ctldl path. |
| Outside validity window | 0x800B0101 CERT_E_EXPIRED - "not within its validity period when verifying against the current system clock or the timestamp in the signed file" | Very often a wrong system clock, not a wrong certificate. Read that message carefully: the clock is named first. |
| Digest mismatch | 0x80096010 TRUST_E_BAD_DIGEST - "the digital signature of the object did not verify"; 0x80096002 TRUST_E_NO_SIGNER_CERT | The file's hash no longer matches the thumbprint in the catalog. Corrupt transfer, a filter driver rewriting the file, or genuine tampering. |
Three of those four classes are completely untouched by anything you do to catroot2. That is the case against the advice: it addresses one class out of at least four, it cannot tell you which one you are in, and it deletes the evidence that would have told you on the way past.
How to verify: eight checks before you stop a service
Work these in order. Each one is read-only. You should be able to stop at whichever step names your fault.
1. Decode the code, and notice which facility it came from. certutil -error resolves the WinTrust and CryptoAPI facilities properly, which is exactly the range you care about here.
That last pair matters. If certutil -error gives you a symbolic name, you are in the trust subsystem and this post applies. If it echoes the number back at you, you are in the Windows Update facility and you should be reading the WU error list instead.
2. Check CryptSvc properly. Not just "is it running" - check start type and identity too. The healthy baseline is Running, Automatic, NT Authority\NetworkService, hosted in svchost.exe -k NetworkService -p. A CryptSvc set to Disabled by a hardening baseline is a real and common root cause, and it produces trust failures that no amount of folder renaming will fix.
3. Ask the file itself. This is the step almost everybody skips, and it is the one that settles the argument. Point Get-AuthenticodeSignature at the actual thing that failed - the .msu, the .cab, the .cat, or the binary named in the log. The shipped SignatureStatus enumeration has exactly seven values: Valid, UnknownError, NotSigned, HashMismatch, NotTrusted, NotSupportedFileFormat, Incompatible. HashMismatch and NotTrusted point at two completely different repairs.
4. Build the chain in isolation. If step 3 returned NotTrusted, take the signer certificate out of the signature object and build its chain by hand, as in the block above. X509Chain reports per-element status through X509ChainStatusFlags, and the flag names are the diagnosis: UntrustedRoot, PartialChain, NotTimeValid, NotSignatureValid, RevocationStatusUnknown, OfflineRevocation, ExplicitDistrust. PartialChain means a missing intermediate. UntrustedRoot means a missing or removed root. OfflineRevocation usually means a proxy, not a certificate.
5. Read the root-update state out of the registry. This is the highest-value check in the whole sequence and it takes one command. The automatic root update mechanism records its own last-success timestamps as binary FILETIME values.
| Value | Meaning | What to look for |
|---|---|---|
LastSyncTime | 8-byte FILETIME of the last successful trusted-CTL sync. | Decode it. Lab device: 2026-08-23 10:29:44 UTC, i.e. today. A value weeks or months old on a device that is otherwise online is your root cause. |
DisallowedCertLastSyncTime | Same, for the untrusted CTL. | Lab device: 10:30:10 UTC, 26 seconds after the trusted CTL. The two sync independently; one can succeed while the other fails. |
EncodedCtl | The cached trusted certificate trust list itself. | Lab device: 204,123 bytes. Present and large means the device has a real CTL cached. Absent or tiny is a red flag. |
DisallowedCertEncodedCtl | The cached untrusted CTL. | Lab device: 6,096 bytes. This is the one Microsoft explicitly recommends you never disable. |
PinRulesEncodedCtl / PinRulesLastSyncTime / PinRulesLastError | Certificate pinning rules cache, its sync time, and its last error. | Lab device: 17,816 bytes, synced 10:30:48 UTC, and a LastError that decodes to 0x2EFD (12029) - a connect failure - stamped 2026-07-30 07:30:36 UTC. A stale error next to a fresh sync time means the failure already recovered. Do not chase it. |
RootDirUrl | Overrides the download location for the CTL files. | Absent on the lab device, so the default ctldl.windowsupdate.com is in use. If present, the CTL files must actually exist at that path or root updates silently stop. |
The policy side lives elsewhere, and it carries a trap worth knowing.
| Value | Meaning | What to look for |
|---|---|---|
DisableRootAutoUpdate | A value of 1 disables automatic update of the trusted CTL. | Absent on the lab device, so root auto-update is on. If it is 1, root updates stopped by policy - and every "delete catroot2" cycle in the world will not bring them back. |
EnableDisallowedCertAutoUpdate | A value of 1 enables automatic update of the untrusted CTL. | Should stay enabled even in disconnected designs. Microsoft recommends against disabling the untrusted CTL specifically. |
Subkeys Certificates, CRLs, CTLs | Where policy-delivered trust objects land. | These three subkeys exist on a device with no root policy at all - see the gotcha below. |
Test-Path 'HKLM:\SOFTWARE\Policies\Microsoft\SystemCertificates\AuthRoot' returns True on the lab device - and that device has no root-certificate policy whatsoever. Measured: ValueCount = 0, SubKeyCount = 3, and the three subkeys are the empty Certificates, CRLs and CTLs containers Windows creates regardless. A detection script that treats key existence as "policy is configured" will report managed root trust on every device in your fleet. Test for the specific value and report the value count.6. Read dberr.txt - and then refuse to over-read it. The catalog database writes its own error log to C:\Windows\System32\catroot2\dberr.txt. It is genuinely useful, and it is also the single most over-interpreted file in this whole area.
Forty-seven error lines. Every August update on this device installed successfully. Decode the codes and the reason is obvious: ERROR_SERVICE_NOT_ACTIVE, ERROR_SHUTDOWN_IN_PROGRESS and the two RPC codes are all what you get when something asks the catalog database a question while CryptSvc is stopping or the machine is shutting down. They are shutdown-race noise, not corruption.
This is the same lesson the CBS.log post in this series lands: an error count is not a verdict. If you are going to cite dberr.txt as your justification for destroying the database, at least decode the errors first - and note that dberr.txt does not appear in any Microsoft product documentation I can find. Its interpretation is entirely community folklore, including the folklore that told you to delete the folder.
7. Check the clock. CERT_E_EXPIRED names the system clock before it names the certificate. A device with a wrong date, a dead CMOS battery, or a broken time source will fail signature validation on perfectly good packages, and will keep failing after you have deleted every folder in System32.
8. Only now, turn on chain logging. Windows ships a detailed certificate-chain diagnostic channel and leaves it off. On the lab device Microsoft-Windows-CAPI2/Operational reports IsEnabled : False with a 1,052,672-byte cap. Enable it in Event Viewer, raise the size first because it fills fast, reproduce the failure, then disable it again. The provider is backed by crypt32.dll and defines 74 events; these are the ones that matter, with task names read from the on-device provider manifest.
| Event ID | Task (from the provider manifest) | What it tells you |
|---|---|---|
| 10 / 11 | Build Chain - Start / Stop | The chain-building call itself. Event 11 carries the result, including the chain error status. This is where UntrustedRoot and PartialChain surface with the actual certificates named. |
| 16 | Inhibit Issuer Certificate Retrieval (Error level) | Windows wanted to fetch a missing issuer and was prevented. Strong signal for a proxy or an offline-hardened build, not a corrupt catalog. |
| 17 / 18 | Retrieve Third-Party Root Certificate from Cache | The root came from the local CTL cache. Healthy path. |
| 19 / 20 | Retrieve Third-Party Root Certificate from Network | The root was fetched live. If these appear and fail, your ctldl.windowsupdate.com path is blocked. |
| 30 | Verify Chain Policy | Policy evaluation on a built chain - the step that applies Authenticode-specific rules. |
| 40 / 41 / 42 | Verify Revocation - Start / Stop; Reject Revocation Information (Error level) | Revocation checking. Event 42 at Error level is the classic offline-revocation false alarm behind proxies. |
| 80 / 81 | Verify Trust - Start / Stop | The WinVerifyTrust call as a whole. Event 81 carries the return value you saw in your install log. |
| 82 | Find Security Catalog for File | The catroot2 event. This is the catalog lookup. If verification fails and event 82 shows no catalog was found for your file, you have a genuine catalog-store problem. If 82 succeeds and 11 fails, the catalog was found and the chain is your problem - stop thinking about catroot2 entirely. |
| 90 | X509 Objects | Dumps the certificates involved so you can match thumbprints against your store. |
The fix: what Microsoft documents, and what deletion actually costs
Microsoft's manual reset procedure is a ten-step sequence, and the shape of it is the argument. Step 2 stops three services - bits, wuauserv, cryptsvc. Step 3 deletes the BITS queue files. Step 4 is the folder rename, and it comes with an unusually blunt instruction: if this is your first attempt, go to step 5 without doing step 4. Microsoft's own words are that step 4 "should only be performed at this point in the troubleshooting if you can't resolve your Windows Update issues after following all steps but step 4".
The rename is not stylistic caution. It is the rollback. If the reset does not fix your problem - which, given that three of the four failure classes are unrelated to the database, is the likely outcome - you stop the services again, delete the freshly rebuilt catroot2, rename catroot2.bak back, and you are exactly where you started with the evidence intact. Delete instead and that option is gone, along with the dberr.txt history that would have told you which class you were in.
What does a rebuild actually cost? Look again at those dberr.txt success lines: the database logs each catalog individually, with a per-file duration, observed between 0 ms and 281 ms. A full rebuild is 7,778 sequential catalog additions on this device, done by a service running as NetworkService and reached over RPC. That is the mechanism behind the CryptSvc CPU and disk complaints that fill community threads after a catroot2 reset - and to be clear about provenance, those complaints are community reports on Microsoft Q&A, not a documented behaviour. The measured per-catalog timings are the documented-adjacent part; the aggregate impact is inference.
There is one more thing worth knowing before you paste the rest of that procedure into a production console.
Twelve of thirty-six regsvr32 lines will fail on a current Windows 11 client, each with its own error dialog or exit code. If you have ever watched that script run and concluded from the error spray that the machine was badly broken, it was not - the procedure is simply older than the OS you ran it on. Say that out loud when a colleague sends you a "Windows Update repair script" that includes all thirty-six.
So what do you actually do, per class?
Untrusted or unbuildable chain. Fix root distribution, not folders. Confirm DisableRootAutoUpdate is not set, confirm ctldl.windowsupdate.com resolves and is reachable on TCP 80, then force a sync. For a disconnected estate, Microsoft documents the two certutil options built for exactly this: Certutil -syncWithWU <destination> pulls the CTL files to a share, and Certutil -generateSSTFromWU <file>.sst produces a serialized store you can distribute by Group Policy. The documented failure codes from that command are themselves diagnostic - 0x80072ee7 means ctldl did not resolve, 0x80072efd means it resolved but would not connect, 0x80070043 means your destination share is wrong.
Outside validity window. Fix the clock and the time source. Nothing else.
Digest mismatch. Re-download the package and check what is touching the file in flight. Microsoft's own guidance for the neighbouring ERROR_SHARING_VIOLATION case points at non-Microsoft filter drivers such as antivirus, and the same suspects apply when a hash stops matching.
No signature found. This is the only one where the catalog store is implicated, and even here the repair is component servicing - DISM /Online /Cleanup-Image /RestoreHealth then sfc /scannow, in that order - because catalogs are installed by the servicing stack and that is what puts a missing one back. Deleting the index does not conjure a missing catalog file.
0x80242006, WU_E_UH_INVALIDMETADATA, "a handler operation couldn't be completed because the update contains invalid metadata". The published fix renames DataStore, Download and catroot2 to .bak. If that is genuinely your code, you have documented cover - and note that even here the documented verb is Ren.One last piece of hygiene, because it will come up in every trust conversation for the rest of 2026. The intermediate in the measured chain above is Microsoft Windows Production PCA 2011, and it expires on 19 October 2026. Microsoft has published guidance on the 2011-era certificate set expiring through 2026 and the 2023 replacements. That guidance is about Secure Boot and the early boot path, and it says plainly that affected devices "will continue to start and operate normally, and standard Windows updates will continue to install". So: real deadline, real work to plan, but not a cause of Windows Update trust failures. Do not let a colleague pin this month's install error on it.
Proof it worked: a healthy trust path, measured
The wrong success criterion is "no errors anywhere". You have already seen a device with 47 error lines in dberr.txt, 14 expired roots, an expired code-signing intermediate and a stale pin-rules connect failure, on which every update installs. Absence of errors is not the target.
The right criterion is four specific positives, and here they are on the lab device.
Check one: CryptSvc Running and Automatic under the right identity. Check two: a named file validating with SignatureType : Catalog and a chain that builds to three elements. That combination proves the catalogs are being found and the chain resolves - which is precisely the pair that events 82 and 11 report, obtained without turning any logging on.
Check three: a LastSyncTime from today. This is the value to collect fleet-wide, because it turns an invisible dependency into a dated fact. A device whose LastSyncTime is three months old is a device whose root trust is frozen, and it will eventually fail to validate something newly signed. That is a proactive finding, not an incident.
Check four is context, not a verdict: fourteen expired roots and a 2026-expired code-signing intermediate are the normal state of a well-maintained Windows 11 device in August 2026.
Then confirm the outcome the way the servicing stack reports it. On this device the Setup log Event 2 records "Package <KB> was successfully changed to the Staged state", and Microsoft-Windows-WindowsUpdateClient/Operational Event 41 records "An update was downloaded" - 55 of them. Those, plus Get-HotFix showing the KB you were fighting, are your proof. Not the absence of lines in dberr.txt.
And if you did end up running the documented reset: verify by renaming your way back. Confirm the update installs, then keep catroot2.bak until the next patch cycle has completed cleanly before you remove it. That is the whole reason Microsoft wrote Ren instead of rd, and it is the reason the internet's favourite version of this fix is the dangerous one.
References
- Additional resources for Windows Update - the ten-step manual reset: the three services, the
Ren ... catroot2 catroot2.bakcommand, the explicit instruction to skip step 4 on a first attempt, and the full 36-entryregsvr32list. - Catalog Files and Digital Signatures - a catalog as "a collection of cryptographic hashes, or thumbprints", installation into
%SystemRoot%\System32\CatRoot, and the instruction that catalog files "shouldn't be added to or removed from that directory manually". - Common Windows Update errors -
0x80242006WU_E_UH_INVALIDMETADATAand its catroot2 rename mitigation, and0x800f081fCBS_E_SOURCE_MISSINGfor the payload class this post is not about. - Configure Trusted Roots and Disallowed Certificates -
ctldl.windowsupdate.comon TCP 80,authrootstl.cab/disallowedcertstl.cab/disallowedcert.sst, theDisableRootAutoUpdateandRootDirUrlregistry values, andcertutil -syncWithWU/-generateSSTFromWUwith their documented error codes. - WinVerifyTrust function - the API behind every
0x800Bcode, exported byWintrust.dll, theWINTRUST_ACTION_GENERIC_VERIFY_V2Authenticode action fromSoftpub.h, and the rule that "no other value besides zero should be considered a successful return". - Time Stamping Authenticode Signatures - why expired certificates in your store are usually harmless: "time stamping allows Authenticode signatures to be verifiable even after the certificates used for signature have expired".
- Get-AuthenticodeSignature - the cmdlet used in checks 3 and 4, and the precedence rule that a catalog signature wins over an embedded one.
- Windows PKI Troubleshooting (CAPI2 Diagnostics) - Microsoft's archived guidance on enabling the CAPI2 operational channel and reading Build Chain results.
- Windows Secure Boot certificate expiration and CA updates - the 2026 dates for
Microsoft Windows Production PCA 2011and friends, and the statement that "standard Windows updates will continue to install".
All device measurements in this post were captured read-only on 23 August 2026 from a live Windows 11 Enterprise 25H2 device, build 26200.9168, servicing stack 10.0.26100.9156. Nothing was stopped, renamed or deleted to produce them. Where a fact is Microsoft-documented it is linked above; where it is an on-device observation or an inference from one, it is labelled as such in the text.