HomeNewsletterCommunityMVP FeedToolsArchiveBlogToday's NewsAboutServicesQuick Links Subscribe free
← Back to Blog
Windows Update Windows UpdateTiWorkerTrustedInstallerCBSServicingDISMComponent StoreTroubleshooting

TiWorker.exe at 100% CPU for an hour: how to tell whether it is hung, or doing exactly what it should

IA
Imran Awan
23 August 2026

The ticket is always worded the same way. "Laptop is unusable, fan at full, Task Manager says Windows Modules Installer Worker is eating the CPU, please kill it." And there is a process called TiWorker.exe sitting there, hour two, doing something nobody can see.

Sometimes it genuinely is wedged. Far more often it is doing exactly the job it exists to do, and ending the task is the single most expensive keystroke available to you.

The short version

"Windows Modules Installer Worker" in Task Manager is TiWorker.exe, the out-of-process worker for the TrustedInstaller service, whose display name is "Windows Modules Installer". Microsoft documents that service's default startup type as Manual, so finding it Stopped and Manual on an idle machine is normal and proves nothing. Component-based servicing is transactional: work is committed only if the whole transaction succeeds. So the question is never "is it using CPU", it is "is it making forward progress" — and you answer that by measuring whether C:\Windows\Logs\CBS\CBS.log is still growing and reading the newest lines, not by guessing. Ending the task mid-transaction is how a device ends up in pending.xml recovery, which Microsoft itself calls an absolute last resort.

The problem: the ticket says kill it, and sometimes that is the worst move available

Two completely different faults produce one identical symptom, and the helpdesk cannot tell them apart from Task Manager.

The first fault is not a fault at all. The servicing stack is mid-transaction — staging a cumulative update, resolving superseded components, or running the component store cleanup that Windows schedules for itself. It will finish. It is slow because the work is genuinely large.

The second fault is real. Something the stack needs is unavailable — a locked file, a permissions problem, a filter driver that never returns — and the worker is spinning on a call that will never return. That one needs intervention.

The reflex response covers both cases with the same three actions: End task on TiWorker.exe, restart the TrustedInstaller service, then delete C:\Windows\SoftwareDistribution for good measure. Two of those do nothing to a CBS transaction, and the third can leave the device in a state that only a reboot-and-rollback can resolve.

Watch out: resetting SoftwareDistribution is the wrong layer entirely. That folder is the Windows Update Agent's download and datastore cache. A transaction already handed to CBS does not live there — it lives in the component store and the CBS registry hive. Deleting it mid-run destroys the Windows Update side of your evidence and changes nothing about the transaction you were worried about.

So before any command runs, three questions need real answers.

  1. Which process is it, which service is hosting it, and is that service state actually abnormal?
  2. Is the transaction making forward progress right now, measurably, or has it stopped?
  3. Who asked for this work, and which package is it on?

Why it happens: one service, one worker, and a transaction you cannot see

The two binaries, and why Task Manager shows you a sentence instead of a filename

Task Manager displays a binary's File Description, not its filename. That is the entire reason nobody recognises these two processes. Here they are on a live Windows 11 device, build 26200.9168, read straight off the files.

PowerShell - run elevated
Get-CimInstance Win32_Service -Filter "Name='TrustedInstaller'" | Select-Object Name,DisplayName,State,StartMode,PathName,StartName Name : TrustedInstaller DisplayName : Windows Modules Installer State : Stopped StartMode : Manual PathName : C:\WINDOWS\servicing\TrustedInstaller.exe StartName : localSystem # The service is named TrustedInstaller. Its display name is Windows Modules Installer. # Stopped / Manual on an idle machine. This is the documented default, not a fault. (Get-Item C:\Windows\servicing\TrustedInstaller.exe).VersionInfo | Select-Object FileDescription,FileVersion FileDescription : Windows Modules Installer FileVersion : 10.0.26100.7019 # And the worker, running from the servicing-stack component directory: Get-CimInstance Win32_Process -Filter "Name='TiWorker.exe'" | Select-Object ExecutablePath,CommandLine C:\WINDOWS\winsxs\amd64_microsoft-windows-servicingstack_31bf3856ad364e35_10.0.26100.9156_none_HASH\TiWorker.exe -Embedding # FileDescription on that file reads: Windows Modules Installer Worker. # That string is what Task Manager shows you. Same thing, different label.

The relationship is simple once you can see it. TrustedInstaller.exe is the service; TiWorker.exe is the worker it activates out-of-process, hence the -Embedding switch. One keeps the books, the other does the labour.

Gotcha: the worker's path is inside C:\Windows\WinSxS, and this device also had three C:\Windows\Temp\SSS_<hex> directories each holding a full copy of the servicing stack — TiWorker.exe, CbsCore.dll, poqexec.exe, wcp.dll and the rest. A signed Microsoft binary running from a random-looking hex directory is exactly the shape EDR flags. It is normal. Microsoft does not document the SSS_ naming, so read no meaning into the hex — but do not treat the location as evidence of compromise either.

What the servicing stack actually is

Microsoft's definition is worth quoting because it explains why one process appears to be responsible for so many unrelated things. The servicing stack is "the component that installs Windows updates", and it "contains the component-based servicing stack (CBS), which is a key underlying component" for DISM, System File Checker, changing Windows features or roles, and component repair.

Four administrative activities, one engine — and the log names that engine on every single load.

C:\Windows\Logs\CBS\CBS.log - genuine excerpt
2026-08-23 10:37:06, Info CBS Starting TrustedInstaller initialization. 2026-08-23 10:37:06, Info CBS No startup processing required, TrustedInstaller service was not set as autostart 2026-08-23 10:37:06, Info CBS Startup processing thread terminated normally 2026-08-23 10:37:06, Info CBS TI: Startup Processing completes, release startup processing lock. 2026-08-23 10:37:06, Info CBS Starting TiWorker initialization. 2026-08-23 10:37:06, Info CBS Ending TiWorker initialization. 2026-08-23 10:37:06, Info CBS Loaded Servicing Stack v10.0.26100.9156 with Core: C:\WINDOWS\winsxs\amd64_microsoft-windows-servicingstack_..._none_HASH\cbscore.dll # Line 2 is the one to memorise. The service was Manual, so there was no pending # cross-reboot transaction to resume. When there IS one, startup processing runs here. # Line 7: cbscore.dll is the engine. It is NOT in System32 - it lives in the WinSxS # servicing-stack component directory, and the version is logged every single time.

Here are the files that matter, all confirmed present on the device rather than copied from another article.

BinaryWhere it actually livesRole
TrustedInstaller.exeC:\Windows\servicing\The Windows Modules Installer service host. Runs as LocalSystem.
TiWorker.exeC:\Windows\WinSxS\amd64_microsoft-windows-servicingstack_...\The worker. File Description "Windows Modules Installer Worker" — the name in Task Manager.
cbscore.dllSame servicing-stack component directory. Not in System32.The CBS engine itself. Its version is written to CBS.log on every load.
poqexec.exeC:\Windows\System32\File Description "Primitive Operations Queue Executor". Applies queued file and registry operations that cannot be done on a running system.
wuaueng.dllC:\Windows\System32\Windows Update Agent. A CBS client, not part of CBS. Version here: 1509.2607.1012.0.
dism.exe / sfc.exeC:\Windows\System32\Two more CBS clients driving the same engine — which is why either can make TiWorker busy.

Why interrupting it is genuinely dangerous

Microsoft's archived engineering write-up on CBS is blunt about the model: updates are grouped into transactions, committed only if every update in the transaction installs without error, and if even one fails the entire transaction is rolled back and no component is updated. It all runs inside a kernel transaction.

That is a safety feature, and it is why the process is slow. It is also why an End task is not a neutral act. You are not stopping a maintenance job; you are aborting a transaction partway through its commit.

When a transaction cannot be completed on a running system, CBS queues the remaining primitive operations and registers poqexec.exe to run them at boot. That registration is visible in the log, and it happened on this device several times in one morning.

C:\Windows\Logs\CBS\CBS.log - genuine excerpt
2026-08-23 10:32:07, Info CSI 0000000c Poqexec successfully registered in [l:12 ml:13]'SetupExecute' 2026-08-23 10:43:15, Info CBS Reboot mark set 2026-08-23 10:45:33, Info CBS Reboot mark cleared 2026-08-23 10:45:33, Info CBS Session: 31273699_3521189603 finalized. Reboot required: no [HRESULT = 0x00000000 - S_OK] # CSI is the component-servicing layer beneath CBS. It registered poqexec against # the SetupExecute value, which Session Manager reads at boot to finish the work. # "Reboot mark set" then "cleared" = the transaction resolved without needing one. # The finalized line with S_OK is the completion record you are looking for.

Microsoft's guidance on what happens when people start editing that machinery is unusually direct. In its article on resolving 0xC0000034 loops it explains that deleting the checkpoint value in pending.xml effectively marks the entire pending operation queue as critical, Windows then believes it has nothing to do and boots normally, and the operations that still needed processing never run — potentially leaving the machine in a worse state. Its conclusion, verbatim, is that this "should be an absolute last resort".

Watch out: that is the destination you are steering towards when you End task on TiWorker during a commit. Not a failed update — a half-applied one, with a queue that no longer matches the store. The devices that end up needing pending.xml surgery are overwhelmingly devices where somebody intervened, not devices that failed on their own.

Why an hour is not automatically suspicious

The most common legitimate cause of a long, quiet, CPU-heavy TiWorker run is component store cleanup, and Microsoft documents exactly when it fires: "The StartComponentCleanup task automatically cleans up components when the system isn't in use."

Read that from a helpdesk perspective. It starts when the user stops typing, and it is still going when they come back and open Task Manager. The task lives at \Microsoft\Windows\Servicing\StartComponentCleanup and reads Ready on a healthy device.

The work is genuinely expensive, and this machine's real figures show why.

199package versions in Superseded state
21reclaimable packages per DISM
14.79 GBbackups and disabled features
Yescomponent store cleanup recommended

Microsoft defines "Number of Reclaimable Packages" as the count of superseded packages that component cleanup can remove, and "Backups and Disabled Features" as the components kept to respond to failures in newer components or to allow enabling more functionality. Neither number is junk. Both are the rollback capability you would want if the next cumulative update went badly.

Context: the scheduled task and the DISM switch are not the same operation. Microsoft documents that when the task runs automatically it waits at least 30 days after an updated component was installed before removing previous versions, and that it carries a 1 hour timeout and "may not completely clean up all files". Running Dism.exe /online /Cleanup-Image /StartComponentCleanup yourself deletes superseded versions immediately with no 30 day grace period and no 1 hour timeout. That is why a manual DISM cleanup can run far longer than an hour and still be perfectly healthy — you removed the timeout.

How to verify: five measurements that separate busy from wedged

Work these in order. Every step is read-only. Touch nothing until step 5 has an answer.

Step 1 — confirm the process and the host, and ignore the start type

Microsoft's service guidance lists TrustedInstaller as always installed with startup type Manual. So Stopped and Manual is the healthy idle baseline — confirmed here — and Manual is still what you see while it runs flat out.

HKLM\SYSTEM\CurrentControlSet\Services\TrustedInstaller
ValueMeaningWhat to look for
StartService start type. 2 = Automatic, 3 = Manual, 4 = Disabled.3 is the documented default, and reads 3 here. A 2 means something promoted it to autostart — your hint that a cross-reboot transaction is queued. A 4 means servicing is disabled and updates will fail.
ImagePathThe service binary.Must be C:\WINDOWS\servicing\TrustedInstaller.exe. Anything else is not this service.
ObjectNameAccount the service runs as.localSystem. A changed account is a common cause of access-denied failures deep in CBS.
DisplayNameIndirect resource string, not literal text.@%SystemRoot%\servicing\TrustedInstaller.exe,-100, which resolves to "Windows Modules Installer". This is why the registry and Services.msc appear to disagree.

Step 2 — measure forward progress, do not judge it

This is the whole diagnosis, and it takes ninety seconds. The Windows Modules Installer service writes to CBS.log, which Microsoft documents as living in %windir%\Logs\CBS. If the file is growing, the transaction is alive.

PowerShell - run elevated - the progress probe
# Sample the log length and the worker's CPU time, wait, sample again. $log = 'C:\Windows\Logs\CBS\CBS.log' $a = (Get-Item $log).Length $c1 = (Get-Process TiWorker -ErrorAction SilentlyContinue).CPU Start-Sleep -Seconds 60 $b = (Get-Item $log).Length $c2 = (Get-Process TiWorker -ErrorAction SilentlyContinue).CPU "log grew {0:N0} bytes; cpu advanced {1:N1} s" -f ($b - $a), ($c2 - $c1) # Real measurement on this device. Before a single read-only DISM query: # TrustedInstaller = Stopped / Manual, no TiWorker process, CBS.log = 11,851,311 bytes # After that one query: # TrustedInstaller = Running / Manual, TiWorker PID 8280, CBS.log = 14,187,664 bytes # A read-only enumeration wrote 2.3 MB of log and burned 15 s of CPU. That is normal.

Two things fall out of that. StartMode stayed Manual while the service was Running, so start type tells you nothing about activity. And a benign read-only query produces exactly the CPU and disk signature the ticket complains about.

Tip: under HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing you will find tempting value names — this device has HangDetect, LastProgress, ExecuteState, PoqTime and PoqCount, plus a SessionsPending subkey. Microsoft does not document any of them. Reading a number out of HangDetect and declaring the process hung is precisely the guesswork this article is arguing against. Look at them for orientation if you like; do not build tooling or a decision on them. The log is documented. Those values are not.

Step 3 — read the newest entries, not the whole file

CBS.log on this device was 11.8 MB before I did anything, with five rotated CbsPersist_*.cab archives beside it. Do not open it in Notepad. Tail it.

PowerShell - run elevated
Get-Content 'C:\Windows\Logs\CBS\CBS.log' -Tail 40 -Wait # Genuine line from this device - this is the single most useful line in the file: 2026-08-23 10:57:36, Info CBS Session: 31273701_3548541904 initialized by client DISM Package Manager Provider # It names the CLIENT that started the work. Clients actually observed in this log: # Arbiter - the Windows Update arbiter, i.e. a real update # WindowsUpdateAgent - the WU agent itself # CbsTask - a scheduled servicing task, e.g. StartComponentCleanup # CbsTaskACR - the automatic corruption repair pass # DISM Package Manager Provider- somebody ran DISM # WinMgmt - a WMI query touched servicing # LanguageResourceClientId - language/FOD servicing # If the client is CbsTask or CbsTaskACR, nobody deployed anything. Windows is # maintaining itself, and there is no deadline to rush.

That one field reframes the ticket. "TiWorker is at 100%" and "the cleanup task Windows scheduled for itself is running" are the same event, described with and without the log.

Step 4 — check the Setup log for package-level movement

CBS writes package state transitions to the Setup event log under the provider Microsoft-Windows-Servicing. Every event and message below was read off this device.

Event IDMessageWhat it tells you
1Initiating changes for package KB####### . Current state is Staged. Target state is Staged. Client id: CbsTask.A transition is starting. The Client id field names who asked — the same client vocabulary as CBS.log. Fresh Event 1s during your window mean forward progress.
2Package <name> was successfully changed to the Staged state.A transition completed. This is the most common event on a healthy device — 240 of them in the recent Setup log here.
4A reboot is necessary before package KB5120708 can be changed to the Installed state.The transaction has gone as far as it can on a running system. Nothing is stuck; it wants a restart. Killing TiWorker will not help.
1013Initiating system store corruption detection and repair. Detection Only: 1, Automatically Triggered: 0.The store is being scanned for corruption. This is legitimately long-running and matches CBS client CbsTaskACR.
1014System store corruption detection and repair has completed. Status: 0x0, Total instances of corruption found: 0, total instances of corruption repaired: 0.The paired completion event. Status 0x0 with zero found is a clean store. This is a real result to quote in the ticket.

Step 5 — find out which packages it is chewing on

Get-WindowsPackage -Online reports package state for the running OS. On this device it gave the concrete reason a cleanup pass takes real time.

PowerShell - run elevated - genuine output
Get-WindowsPackage -Online | Group-Object PackageState | Select-Object Count,Name Count Name ----- ---- 199 Superseded 121 Installed 95 Staged # And what that looks like for one single package name: Microsoft-OneCore-ApplicationModel-Sync-Desktop-FOD-Package~...~10.0.26100.1742 Staged Microsoft-OneCore-ApplicationModel-Sync-Desktop-FOD-Package~...~10.0.26100.8655 Superseded Microsoft-OneCore-ApplicationModel-Sync-Desktop-FOD-Package~...~10.0.26100.8875 Superseded Microsoft-OneCore-ApplicationModel-Sync-Desktop-FOD-Package~...~10.0.26100.9168 Installed # One component, four tracked versions, two of them superseded. Multiply by 199. # THAT is the 14.79 GB of backups, and that is what cleanup has to walk. # Note this command itself starts TrustedInstaller and spins up TiWorker.

Now decide. It is working if the log grew, the worker's CPU time advanced, and the newest log timestamp is current. It is stalled only if, across a 15 minute window, the log length, the newest timestamp and the worker's cumulative CPU all failed to move and no new Setup events appeared. All four, measured twice. Anything less is a guess.

The fix: let it finish, or unwind it in the right order

Case A — it is working. Do nothing, and manage the human instead.

This is the majority case, and the correct action is to leave it alone. Tell the user it is Windows maintaining itself, that the machine is usable, and hand them the log tail as a completion signal they can watch.

Do not reboot to "clear it" either. A restart during a commit lands you where an End task does, only with less log. If a reboot is genuinely needed, Event 4 says so explicitly.

Case B — all four progress signals are flat. Unwind it properly.

The order matters, because each step gives CBS a chance to close its own books before the next one takes that chance away.

PowerShell - run elevated - in this order only
# 1. Capture the evidence BEFORE you change anything. You cannot get it back. Copy-Item 'C:\Windows\Logs\CBS\CBS.log' "$env:TEMP\CBS-preintervention.log" Get-WinEvent -LogName Setup -MaxEvents 200 | Export-Clixml "$env:TEMP\setup-log.xml" # 2. Ask the SERVICE to stop. Do not End task the worker. # A service stop request lets CBS run its finalization path; End task does not. Stop-Service -Name TrustedInstaller # 3. Confirm in the log that it unwound cleanly rather than being severed. Get-Content 'C:\Windows\Logs\CBS\CBS.log' -Tail 8 # 4. Reboot. This is where queued primitive operations are applied by poqexec.exe # and where startup processing resumes a cross-reboot transaction. Restart-Computer # 5. ONLY after a clean boot, and only if DISM said cleanup was recommended: Dism.exe /Online /Cleanup-Image /AnalyzeComponentStore Dism.exe /Online /Cleanup-Image /StartComponentCleanup

Step 2 is the one people skip. A service stop is a request the service can answer; End task is not. The difference shows up in the log, as the next section demonstrates.

Step 5 is last and conditional. If /AnalyzeComponentStore reports cleanup as recommended — as it does here, with 21 reclaimable packages — /StartComponentCleanup is the documented remedy, and Microsoft explicitly warns against disabling component cleanup at all.

Gotcha: do not reach for /ResetBase to make it faster. Microsoft's warning is that after /StartComponentCleanup /ResetBase completes, "All existing update packages can't be uninstalled". You would be trading your ability to roll back a bad update for disk space, on a machine you already suspect has a servicing problem. That is the wrong trade at the worst possible moment.

What not to do, in order of how often it is done

The reflexWhy it does not help here
End task on TiWorker.exeAborts a kernel transaction mid-commit. This is the direct route to a store and queue that disagree with each other.
Delete or reset C:\Windows\SoftwareDistributionWrong layer. That is the Windows Update Agent's cache, not CBS state. Destroys WU-side evidence, changes nothing about the transaction.
Run sfc /scannow firstSFC is another CBS client. You are adding a second caller to an engine you think is stuck, and its output floods the log you are trying to read.
Set TrustedInstaller to DisabledMicrosoft's own note: disable it and install or uninstall of updates might fail. You have turned a slow device into an unpatchable one.
Edit or delete pending.xml, or delete SetupExecuteAbsolute last resort by Microsoft's own description, and it can leave the machine worse off because operations that still needed processing silently never run.

Proof it worked: what a clean finish actually looks like

A completed servicing session has a distinct signature, and it is worth knowing by sight because it is your evidence for closing the ticket. This is the genuine tail of CBS.log from this device as the worker shut down normally.

C:\Windows\Logs\CBS\CBS.log - genuine clean-exit tail
2026-08-23 10:47:36, Info CBS CbsCoreFinalize: WcpUnload 2026-08-23 10:47:36, Info CBS CbsCoreFinalize: TurboStackUnload 2026-08-23 10:47:36, Info CBS CbsCoreFinalize: WdsUnload, logging from cbscore will end. 2026-08-23 10:47:36, Info CBS Ending TiWorker finalization. 2026-08-23 10:47:36, Info CBS Ending the TrustedInstaller main loop. 2026-08-23 10:47:36, Info CBS Starting TrustedInstaller finalization. 2026-08-23 10:47:36, Info CBS Winlogon: Stopping notify server 2026-08-23 10:47:36, Info CBS Ending TrustedInstaller finalization. # Read the order. The WORKER finalizes first, then the service main loop ends, # then the service finalizes. That ordered unwind is what an End task destroys. # If your log ends abruptly mid-session with no finalization lines, you now know # somebody killed it - and that is a root cause worth writing in the ticket.

Alongside it, four independent confirmations. Each is a real result from this device, not an example.

CheckHealthy result on this device
Get-Service TrustedInstallerStatus Stopped, StartType Manual — back to the documented idle baseline once work finished.
Get-Process TiWorkerNo process. The worker exits; it is not meant to be resident.
Setup log, Event 1014Status 0x0, 0 instances of corruption found, 0 repaired.
Get-ScheduledTaskInfo \Microsoft\Windows\Servicing\StartComponentCleanupState Ready, LastTaskResult 0, LastRunTime 2026-08-22 13:37:21.

That last row is worth a second look, because it closes the loop on the whole article. The scheduled task reported it last ran at 13:37:21 on 22 August. Independently, DISM /Online /Cleanup-Image /AnalyzeComponentStore on the same device reported "Date of Last Cleanup : 2026-08-22 13:37:36".

Fifteen seconds apart. Two unrelated tools, one event. The scheduled task Windows runs by itself, when the system is not in use, is demonstrably the thing that performed the cleanup DISM is reporting. Nobody deployed anything. Nobody did anything wrong. And if a user had opened Task Manager at 13:37 on 22 August, they would have filed the exact ticket this article started with.

11,851,311CBS.log bytes before one read-only query
14,187,664CBS.log bytes after it
15.3 sTiWorker CPU consumed doing nothing but reading
ManualStartType, unchanged, while Running

Measure, then decide. The log is the only thing in this stack that will tell you the truth, and it is free.

References

Every command output, service state, registry value, log excerpt, event message and package count above was captured read-only from one live Windows 11 device, build 26200.9168, servicing stack 10.0.26100.9156. Nothing was staged.

Was this post helpful?
React below — no account needed
Share this post
LinkedIn X / Twitter Reddit Bluesky

More from EndpointWeekly

Windows Update
Error 0x800f081f is not a verdict: find the missing payload in…
CBS_E_SOURCE_MISSING means servicing wanted a payload and could not find it - not that…
Windows Update
The Windows patching triage decision tree: which log, which key,…
Route each patching symptom to the one evidence source that answers it. Then learn the…
Windows Update
The KB installed fine and is still being offered: supersedence,…
One 25H2 device held 199 Superseded, 121 Installed and 95 Staged packages while…