An update fails. You do the right thing and open C:\Windows\Logs\CBS\CBS.log. Twenty seconds later you close it again, because it is a wall of text with no obvious beginning and no obvious error, and somebody on the ticket has already typed "just run SFC".
That reflex is the problem. SFC writes into the same log you were about to read.
CBS.log is written by the servicing stack, not by Windows Update, so a download failure leaves it empty and an install failure leaves the reason in it and nowhere else. Microsoft's documented technique is to search the newest CBS.log for , error — with the leading comma, because that comma is a column separator. On the healthy Windows 11 device measured here that returns zero hits across 73,450 lines. But the rotated log from the day the August cumulative installed holds 715 Error-level lines, and it succeeded. An error line is a lead, not a verdict.
The problem: 73,450 lines and you need one of them
Every number here was measured read-only on one live corporate device: Windows 11 Enterprise, build 26200.9168, servicing stack 10.0.26100.9156. Nothing was repaired to produce them.
Idle from a servicing point of view, CBS.log was 11,851,311 bytes and 73,450 lines covering under two days. The previous rotated log, covering the evening the August cumulative actually installed, was 1,655,357 lines.
CBS.log is not cryptic. It is enormous, and almost all of it is normal. So two reflexes take over, and both cost you the evidence.
The first is sfc /scannow. Microsoft documents that SFC "writes the details of each verification operation and of each repair operation to the CBS.log file", tagging each one [SR], and that it verifies files in groups of 100. A full pass appends thousands of lines to the file you needed — and on a log that rotates by size, that can push the failing session out of CBS.log into a compressed archive.
The second is renaming SoftwareDistribution and catroot2. That does not overwrite CBS.log, but it does something worse. It makes the failure unreproducible.
Three questions come before either command.
- Which log file contains the failure, given that CBS.log rotates?
- Which session inside it failed, and which client started it?
- Which error in that session is the first, rather than the hundredth consequence of it?
Why it happens: CBS.log belongs to the servicing stack
Windows Update does not write this file
Microsoft's log table is blunt about the division of labour. The Windows Update client logs through ETW and you reconstruct WindowsUpdate.log with Get-WindowsUpdateLog. The Update Orchestrator logs to C:\ProgramData\USOShared\Logs. And CBS.log, at %systemroot%\Logs\CBS, is the log that "provides insight on the update installation part in the servicing stack".
That is the whole diagnostic split. If the update never downloaded, CBS.log has nothing to say. If it downloaded and failed to install, CBS.log is the only file with the reason.
The log proves its own ownership, because every session records who opened it. Across the 183 sessions in that 73,450-line log: WinMgmt 126 (WMI queries from inventory, ConfigMgr and monitoring agents), DISM Package Manager Provider 30, WindowsUpdateAgent 11, Arbiter 8 (applicability and supersedence), LanguageResourceClientId 6, and one each from CbsTask and CbsTaskACR, the store maintenance and automatic repair tasks.
Eleven sessions out of 183 came from Windows Update. Assume everything in CBS.log is an update and you will spend an hour reading WMI inventory calls.
The binaries, and where they actually are
| File | Measured location and version | Role |
|---|---|---|
TrustedInstaller.exe | C:\Windows\servicing\ — 10.0.26100.7019 | Service host, display name Windows Modules Installer. Holds the lock, owns the transaction. |
TiWorker.exe | C:\Windows\WinSxS\amd64_microsoft-windows-servicingstack_31bf3856ad364e35_10.0.26100.9156_none_HASH\ | The worker process. Task Manager calls it "Windows Modules Installer Worker". |
CbsCore.dll | Same WinSxS directory as TiWorker.exe | The engine. CBS.log names the copy it loaded. |
poqexec.exe | C:\Windows\System32\ — 10.0.26100.9156 | FileDescription "Primitive Operations Queue Executor". Runs at boot the work impossible while Windows runs. |
CbsApi.dll, CbsMsg.dll, wrpintapi.dll | C:\Windows\servicing\ | CBS client API, message resources, Windows Resource Protection internals. |
sfc.exe / dism.exe | C:\Windows\System32\ — 7019 / 8457 | Both drive CbsCore.dll and log to CBS.log. |
Note where TiWorker.exe is not. Article after article says it lives in C:\Windows\servicing beside TrustedInstaller.exe. On this device that folder holds four files and TiWorker.exe is not one of them.
The reason matters. TrustedInstaller.exe is a fixed host in a fixed path. TiWorker.exe and CbsCore.dll ship as a WinSxS component named microsoft-windows-servicingstack, so a servicing stack update replaces the engine by dropping in a new component directory. That is why CBS.log announces the engine it loaded, by full path, at the top of every session.
TrustedInstaller.exe. Measured here, TrustedInstaller.exe is 10.0.26100.7019 while the loaded stack is 10.0.26100.9156 and poqexec.exe is also 9156. Those versions do not move together. The only authoritative statement of which engine ran is the Loaded Servicing Stack line in the log.A session begins, a session ends
The unit of work is the session, bracketed by two line shapes: Session: 31273391_2702078006 initialized by client WinMgmt, and later Session: 31273393_2310201486 finalized. Reboot required: no [HRESULT = 0x00000000 - S_OK].
Those numbers are not random. They come from SessionIdHigh and SessionIdLow under the Component Based Servicing key. The session ID is your grep key: filter to one and you have isolated a single transaction end to end.
The component tags: CBS, CSI, SXS, DPX
| Tag | What emits it | A failure there means |
|---|---|---|
CBS | Top-level engine in CbsCore.dll: sessions, packages, applicability, staging, reboot decisions. | A package-level problem. Where CBS_E_ codes appear. |
CSI | Component Servicing Infrastructure: store, files, manifests. SFC's [SR] entries are CSI lines. | A file, manifest or hydration problem below the package layer. |
SXS | Side-by-side assembly handling, including TurboStack hydration. | Usually a hash or "winner version" mismatch on one file. |
DPX | Delta and patch expansion. Appears only during payload work, and only when EnableDpxLog is set. | Payload not fetched, or a forward delta failed. |
Measured proportions in the 1,655,357-line install-day log: 1,535,153 CBS Info, 73,378 SXS Info, 32,284 CSI Info, 305 DPX Info — and 385 CSI Error plus 330 SXS Error.
Now the important part. That update succeeded.
Two structural details pay for themselves. 00000019@2026/8/19:17:19:23.508 is a monotonic counter plus a UTC timestamp in milliseconds, reading 17:19:23 where the line's own local stamp reads 18:19:23. And storelayout.cpp(2237) is the source file and line inside the servicing code: how you tell near-identical errors apart.
[Warning,Facility=...] tag, treat it as a warning whatever the level column says, and go looking for one whose bracket says [Error,...] instead. Sorting candidates by that internal severity, then by the CSI counter, beats scrolling.Rotation: CBS.log is only the newest slice
Microsoft's DISM article names CBS.log and CBS.persist.log. On modern Windows 11 the second is not what you find. You get timestamped archives, and the log documents its own rotation.
The timestamp in the filename is the piece nobody tells you about, and it is the easiest way to open the wrong file. CbsPersist_20260819174029.cab reads as 17:40:29. The first line inside it is 2026-08-19 17:03:21; the last is 2026-08-19 18:40:03. So the filename is neither the start of the contents nor local time. It is the UTC moment of rotation, which is the end of the data inside — 18:40:29 local, twenty-six seconds after that last line. So for a failure at 18:15 local on a UTC+1 device, take the archive whose stamp is the next one after 17:15.
How to verify: the numbered read-only path
Nothing here changes the device. Do all of it before any repair.
1. Get the offset. Find the Universal Time is: line in the newest CBS.log and compare it to the local stamp on the same line. That offset drives everything below.
2. Confirm this is a servicing failure at all. If the update never downloaded there is no WindowsUpdateAgent session in the failure window. Check the download side with the event log, not the log tool: Microsoft-Windows-WindowsUpdateClient/Operational Event 26 ("successfully found N updates") and Event 41 ("An update was downloaded") were both present here. No Event 41 means CBS.log is a dead end.
Get-WindowsUpdateLog -ForceFlush stops the Update Orchestrator and Windows Update services to flush their traces. That is a service outage mid-incident, and it can change the state you were trying to capture. Read the event channels first.3. Pick the right file. Convert the failure time to UTC, then take the CbsPersist_* whose stamp is the first one after it, or CBS.log if nothing has rotated since.
4. Run Microsoft's own filter. Quoting the mitigation for 0x80070005: go to %Windir%\logs\CBS, open the last CBS.log, search for , error, and match with the timestamp. The leading comma anchors the search to the level column instead of matching "error" inside ordinary messages.
Look at the shape of those four lines. One root event, four entries, each a caller one frame further up re-reporting the same HRESULT. That is the cascade. In a real failure it runs to dozens of lines, and the useful one is always the first: deepest, earliest, most specific. Everything after is a caller saying "what I called failed".
5. Isolate the session. Filter the log to the session ID nearest your first real error. You now have one transaction, from initialized by client to finalized ... Reboot required.
6. Read the CBS registry state. The servicing stack's own opinion of its health, and far faster than the log.
| Value | Meaning | What to look for |
|---|---|---|
Corrupt | Store corruption flag set by the engine. | 0 measured. Non-zero means the engine already decided the store is damaged. |
AutoRepairNeeded | Automatic corruption repair queued. | 0 healthy. 1 means CbsTaskACR will repair on its own schedule. |
HangDetect | Watchdog flag for a stalled transaction. | 0 healthy. Non-zero pairs with 0x800F0920 CBS_E_HANG_DETECTED. |
SessionIdHigh / SessionIdLow | The two halves of the current session ID. | Measured 31273701 / 3548541904. Join with an underscore for your grep key. |
EnableLog / EnableDpxLog | Whether CBS and DPX logging are on. | Both 1 by default. EnableLog at 0 means your evidence is off. |
PoqCount / PoqTime | Primitive Operations Queue runs and seconds at boot. | Measured 1 / 48. A large PoqTime is where "Working on updates" goes. |
PreviousCorruptionDetected | FILETIME of the last corruption event. | Measured 134173478872001427 = 2026-03-07 09:04:47 UTC. Check it before blaming this update. |
SessionsPending\CrossRebootHresult | Result of a transaction spanning a reboot. | 0 healthy. Non-zero is the failure code of a reboot-spanning install: the most valuable number here. |
RebootPending (subkey) | Volatile key marking a pending reboot. | Absent when clean. Present, and the log says "RebootPending volatile key indicates that a reboot is pending, skip startup processing". |
The Packages subkey under that parent held 7,490 entries. That is the registry side of the store, and what the 0x800f0831 article means by an update that is installed "but some packages aren't applied to the registry".
poqexec.log is not proof a boot-time queue ran. It appeared 21 times in two days on this device with no pending reboot, because it records queue creation during the online session. Use PoqCount and the RebootPending key instead.7. Correlate against the Setup event log. This turns a wall of text into a timeline. Microsoft's corruption article says plainly you can check the Setup event log for errors. It emits one clean line per package state change where CBS.log emits ten thousand.
Channel: Setup (Windows Logs › Setup), provider Microsoft-Windows-Servicing.
| Event ID | Message observed on the device | What it tells you |
|---|---|---|
1 | "Initiating changes for package KB. Current state is Staged. Target state is Staged. Client id: CbsTask." | A transition is starting. Client id is the same client name CBS.log uses: your join key. |
2 | "Package KB was successfully changed to the Staged state." | The package reached Staged. A last Event 2 of Staged with no later Installed identifies the KB that was mid-flight. |
4 | "A reboot is necessary before package KB5120708 can be changed to the Installed state." | Parked awaiting reboot. Pair with Reboot required: yes in CBS.log. |
1013 | "Initiating system store corruption detection and repair. Detection Only: 1, Automatically Triggered: 0." | A store scan started. Detection Only: 1 is read-only; Automatically Triggered: 0 means a human started it. |
1014 | "...completed. Status: 0x0, Total instances of corruption found: 0, total instances of corruption repaired: 0." | The verdict, without opening CBS.log. |
8. Only now map the code.
| Code | Documented name | What it points at |
|---|---|---|
0x800f0805 | CBS_E_INVALID_PACKAGE | At Info level, a probe for a package that is not installed. Noise. |
0x800f0831 | CBS_E_STORE_CORRUPTION | A package missing from the store, or not applied to the registry. |
0x800f081f | CBS_E_SOURCE_MISSING | ResolveSource() failed. Nothing good to copy from. |
0x80073712 | ERROR_SXS_COMPONENT_STORE_CORRUPT | Store inconsistent, surfaced from the SXS layer. |
0x800f0821 | CBS_E_ABORT | Transaction timeout. The answer is more CPU and memory, not repair. |
0x800F0920 | CBS_E_HANG_DETECTED | Documented as a "subsequent error logged after getting 0x800f0821". A cascade, officially. |
0x80070020 | ERROR_SHARING_VIOLATION | "Numerous causes. CBS log analysis required." Microsoft blames third-party filter drivers. |
0x800f0984 / 0x800f0982 / 0x800f0986 | PSFX_E_MATCHING_BINARY_MISSING / PSFX_E_MATCHING_COMPONENT_NOT_FOUND / PSFX_E_APPLY_FORWARD_DELTA_FAILED | The hydration and delta family: codes on parallelhydrator.cpp lines. |
certutil -error is the usual offline decoder and it works for Win32 codes — certutil -error 0x80073712 really returns "The component store has been corrupted". Against the CBS facility it gives you nothing. Measured here, certutil -error 0x800f0831 returns only "Error message text: Error 0x800f0831", and the same for 0x800f0805, 0x800f081f, 0x800f0821 and 0x800f0983. The symbolic name for those already sits in CBS.log next to the code. That is a large part of why you read the log instead of the code.The fix: let the log choose the remediation
You now have a code, a package, a session and a timestamp. Enough to pick a remediation, not guess one.
Store-corruption codes — 0x800f0831, 0x800f081f, 0x80073712, 0x80070570, 0x80073701, 0x8007371b — all get the same documented action: Dism.exe /Online /Cleanup-Image /Restorehealth, then Sfc.exe /Scannow, then restart. That order is not a preference. SFC repairs live system files from the store, so if the store is the broken thing SFC has nothing good to copy.
If DISM itself fails, read its summary block in CBS.log rather than the console: Operation result, Last Successful Step, and separate counts for CBS Manifest, CBS Metadata, CSI Manifest, CSI Metadata and CSI Payload corruption. Microsoft's advanced guide walks from a corrupt file path to its UBR, to a KB, to an Update Catalog download you feed back with /Source and /LimitAccess.
Timeouts — 0x800f0821 and its follow-on 0x800F0920 — get no repair at all. Microsoft's mitigation is more CPU and memory, particularly on VMs.
Sharing violations, 0x80070020: the log names the file. Filter Process Monitor on that filename during a repeat attempt to find the driver holding it.
TrustedInstaller service set to Manual is completely normal, and a frequent false alarm. But Microsoft documents a real trap under 0x80070bc9 ERROR_FAIL_REBOOT_REQUIRED: TrustedInstaller changes its own startup type from Manual to Automatic when it has a transaction to process after a restart, and if a GPO reverts it to Manual before that restart the transaction can never be applied — blocking every subsequent update. Manual and stopped at idle is fine. Manual enforced by policy with work pending is a hard block.C:\Windows\Logs\CBS folder somewhere else, cabs included. It costs seconds, and the next engineer still has the original transaction after your fix changes the symptom.And the things not to do first. Do not open with SFC; it appends thousands of [SR] lines to the log you have not read. Do not rename SoftwareDistribution or catroot2 before you have identified the failing package — that is a documented mitigation for specific download and metadata errors such as 0x80242006, not a general fix, and it discards the reproduction. Do not delete anything from WinSxS. And do not run /ResetBase mid-investigation: it removes your ability to uninstall the update you are investigating.
Proof it worked: the healthy baseline, measured
You cannot recognise a broken servicing stack until you know what a healthy one looks like. Here is the full read-only signature.
One more correlation closes the loop. DISM reports the last store cleanup at 2026-08-22 13:37:36. CBS.log's Maint: end archive logs line is stamped 2026-08-22 13:37:35. One second apart, because the task that archives the logs is the one that tidies the store.
Notice what "healthy" does not mean. Not zero non-zero HRESULTs: there were 40. Not every session finalised: 141 did not. Not a tidy store: DISM recommends cleanup. Healthy means zero Error-level lines, the corruption flags at zero, and no pending cross-reboot transaction.
Capture that baseline from a known-good build and keep it beside your patch runbook. The next time an update fails you will not be reading 73,450 lines. You will be reading the handful that differ.
References
- Windows Update log files - the table naming CBS.log the servicing-stack install log, and the ETL and USO logs.
- Analyze log file entries that SFC.exe generates - SFC writing into CBS.log with
[SR]tags, and the findstr command. - Common Windows Update errors - every symbolic name in the code table, the "search for
, error" technique, and the TrustedInstaller GPO trap. - Fix Windows Update corruptions and installation failures - the corruption code table, the Setup event log instruction, and the DISM summary format.
- Troubleshoot Windows installation error 0x800f0831 - CBS_E_STORE_CORRUPTION and the Component Based Servicing
Packagessubkey. - Servicing stack updates - the definition of the servicing stack and of CBS.
- Get-WindowsUpdateLog - for when CBS.log turns out to be the wrong file.
- Manage the Component Store - and the explicit warning against deleting anything from WinSxS.
- Understanding Component-Based Servicing - Microsoft's background piece, linked from the servicing stack docs.