Which updates does this machine need? The question sounds like a database lookup, and it is one of the hardest in endpoint management, because every source you would trust to answer it is wrong in a different way. This is a long, technical walk through why that is true and what a correct answer requires. It is written for people who patch fleets for a living.
When we started building patch management for Windows, we assumed the applicability problem was already solved. Windows ships with an entire update stack. There is a documented client API for it. There is a Settings page any user can open. Microsoft publishes a catalog of every update it has ever released. Surely, somewhere in there, is an authoritative answer to "does KB5066835 apply to this machine?"
There is not. Not in any one of those places. We spent months learning that the hard way, one production incident at a time, and the conclusion we reached is the thesis of this post: on Windows, applicability has to be computed from primary evidence and then reconciled against reality, because there is no single source of truth to look it up in.
To be precise about the claim, because precision matters here: the Windows update stack is a remarkable piece of engineering that keeps a billion machines patched, and the problem I am describing is narrower and more interesting than "the Windows Update Agent is bad." Each of the three sources you would reach for to answer the applicability question, the Windows Update Agent COM API, the Windows Update Settings UI, and the Microsoft Update Catalog's own metadata, is authoritative for a slightly different question than the one you are asking. When you use any of them as your answer, the gap between the question they answer and the question you asked shows up as a wrong patch decision. Sometimes that means offering an update that will never install. Sometimes it means hiding one the machine needs.
The first half of this post is the three sources and how each one diverges. The second half is the mechanism that works, built up in layers. Throughout, the examples come from production: KB numbers, error codes, and build numbers out of our own incidents.
Part 1: the three false authorities
False authority #1: the Windows Update Agent COM API
The Windows Update Agent, WUA, is the client-side engine that Windows Update itself runs on. It exposes a COM automation surface: Microsoft.Update.Session, IUpdateSearcher, IUpdateInstaller, and friends. It is what WSUS clients talk to, it is a large part of what Intune drives, and it is the first thing nearly every RMM reaches for, because it is right there and it is what Windows uses. We started there too. Our very first update code was PowerShell calling New-Object -ComObject Microsoft.Update.Session, and we later ported the same calls into a native Rust module against the COM interfaces directly.
WUA is good at what it is designed for. The trouble starts when you treat its answers as ground truth for whether an update applies and whether an install succeeded. Two divergences, both of which cost us time.
The install "succeeded" but the machine did not change. When you call IUpdateInstaller.Install() for a cumulative update, the immediate result you get back can be orcSucceeded (that is OperationResultCode value 2). It is tempting to treat that as "the update is installed." For any update that requires a reboot, that return tells you only that the pre-reboot phase, the staging of the payload into the component store, completed. The real work, where Component-Based Servicing (CBS) commits the new component versions into the running OS, happens during the reboot, in a kernel-mode servicing pass you have no synchronous handle on.
If CBS rejects the update during that reboot pass, and it can, for reasons that have nothing to do with whether WUA "installed" it, the update rolls back. The build number never advances. And here is the part that makes automation dangerous: WUA's own history will, after the fact, log that same update as orcFailed (OperationResultCode 4). So the API told you orcSucceeded at install time and orcFailed after the reboot, for one update, and if your tooling trusted the first answer it now believes a machine is patched that is not. We watched this exact pattern on Windows 11 24H2 with a cumulative update (KB5066835) staging cleanly on build 26100.1742 and then failing to commit. The API was answering "did staging succeed?" while we were asking "is this machine patched?" Those are different questions, and WUA does not distinguish them for you at the point where you make the decision.
Supersedence it will not reliably tell you. A superseded update is one that a newer update fully replaces. You should never offer a superseded cumulative update, because the newer one already contains it, and trying to install the old one on top of the new one produces failures. WUA is supposed to filter these out of a search. In practice, it does not always. We hit a case where a September cumulative update (KB5065426) had been superseded by the October one (KB5066835), and WUA kept returning the September update as installable. Installing it produced an infinite loop: it would stage, fail to commit against the newer components already present, and offer itself again.
The obvious fix is to ask WUA whether an update is superseded. Here is the catch: on the client, it cannot tell you. The update objects a scan hands back expose SupersededUpdateIDs, the list of updates that this update supersedes, but there is no property that answers the question you have, which is whether this update has itself been superseded by something newer. That boolean, IsSuperseded, exists only on a different, server-side interface for administering a WSUS server, which is not what you are holding when you scan an endpoint. So to decide whether the September update is stale, you have to reconstruct the answer yourself, by walking the SupersededUpdateIDs of every other update to see whether any of them names it. And that reverse-direction data is produced by the same WUA bookkeeping that just failed to hide the September update in the first place. The signal you would use to correct the problem comes from the same subsystem that has the problem.
Add to this the operational friction: WUA discovery and install want administrative privileges (in practice that means running as the SYSTEM account, which is what the built-in Windows Update service itself uses), its failures are frequently silent, and its state can be internally inconsistent in ways that are hard to reproduce. Early in our build, our own engineering roadmap listed "dealing with WUA quirks: silent failures, inconsistent state" as a named challenge before we had even finished the native port. The API is a fine installer backend for a narrow set of update types. Treating it as your source of truth for applicability is where it breaks.
False authority #2: the Windows Update Settings UI
So if the API is unreliable, trust the UI. This was, briefly, our documented conclusion. One of our early internal lessons, written down while we were still WUA-based, read "trust Windows Update UI as ground truth: if Settings does not show an update, do not offer it." The Settings page is what an administrator would look at to sanity-check a machine, and it felt like the closest thing to what the human sees.
Then the UI itself proved unreliable, in more than one way.
The frozen ghost. We spent several days on a Windows Server 2016 endpoint whose native "Check for updates" button was greyed out and whose update list looked stuck. We went through theory after theory and falsified each one empirically: our own agent must be suppressing it (no), a customer policy object (no), a domain Group Policy (no), a NoAutoUpdate registry value (we deleted it live and the button stayed greyed). The cause was that the Settings page was displaying a cached state from the last successful scan, which had happened roughly ten months earlier, and had frozen there when the underlying scanner broke on a servicing-stack failure. The UI never re-evaluated live policy or live state while the scanner behind it was broken. It was showing a ghost: a snapshot of what the machine believed months ago, presented as if it were current. Anyone reading that page to determine what the machine needed today would have been badly misled.
The page reads a cache. Healthy machines have a version of this too. In our experience, the Windows Update Settings page and its policy state read from a cached snapshot (the values under the update policy's GPCache / PolicyState) rather than from the live policy hive that governs the scanner, and that cache does not reliably refresh when you restart the update service; it gets rebuilt on a Group Policy processing pass. So there is a window in which the scanner is already behaving according to new policy while the Settings page banner and buttons still show the old world. The visible UI lags the true state. (There are OS-version wrinkles even to this: the cache mechanism works differently on Server 2016, where the page reads the live hive directly. The specific registry path is beside the point. What matters is that "what the Settings page shows" and "what the machine will do" are two different things that can disagree.)
It hides prerequisites on purpose. Consumer Windows Update is designed to make patching invisible, and one of the ways it does that is by silently installing prerequisites. Cumulative updates increasingly depend on a matching Servicing Stack Update (SSU) being present first. The Settings UI will quietly install the SSU ahead of the cumulative and never mention it. That is good for a consumer. It is a problem for anyone building a tool that has to reason about install order, because the UI hides a dependency that exists and that will cause a hard failure if you get the order wrong. The abstraction is deliberate, and it leaves an enterprise tool blind to something it has to know.
It cannot see what it did not install. The deepest problem with treating the UI (or WUA under it) as authoritative for "is this already installed" is that both of them reason primarily about updates that flowed through their own channel. An update applied by wusa.exe from a downloaded .msu, or by DISM adding a package directly, or by a third-party patching tool, or by an OEM image, is present on the machine and fully effective, but it did not necessarily leave the bookkeeping that WUA's scan and the Settings UI key off of. The result is a machine that has an update installed, by some mechanism, while the UI is prepared to offer it again as "needed." Any environment that has ever done an offline patch, a manual .msu, or a coexisting tool during a migration has machines in this state. The UI is authoritative for one question only: "what did I, Windows Update, do." It has nothing to say about what is true of the machine.
False authority #3: the Microsoft Update Catalog's own metadata
If the client is unreliable, go to the source. Microsoft publishes the Microsoft Update Catalog, the definitive list of every update, with metadata: which products each applies to, what it supersedes, its classification, its architecture. This is the raw material any serious patch tool has to ingest, and we do ingest it. But the catalog's own metadata is not internally consistent, and if you trust it at face value you will make wrong decisions.
Supersedence is not transitive in the data. The catalog records supersedence, but it records only direct predecessors, not the full chain. If update C supersedes B and B supersedes A, the catalog page for C frequently lists only B, not A. For cumulative updates that are cumulative by definition, this is a serious gap: a .NET cumulative from October 2025 (KB5066136) listed only a couple of direct predecessors, not the chain reaching back years. If you treat "is this in some update's supersedes list" as your test for "is this superseded," you will keep offering ancient updates that are transitively, but not directly, superseded. You have to compute the transitive closure yourself; Microsoft's per-update metadata does not state it.
Cross-product relationships are mislabeled. Windows 10 version 1607 and Windows Server 2016 share a build lineage (the 14393 series). Starting in late 2025, Microsoft began publishing unified cumulative updates for that lineage under generic "Cumulative Update for Windows 10" titles, whose supersedes metadata listed only the Windows-10-titled predecessors, not the Server-2016-titled ones. The effect in the raw data was that a Server 2016 update (KB5070882) stayed marked as not-superseded long after it had been, because the superseding update's metadata never named it. The relationship held on the machine; the catalog failed to encode it across the product boundary.
Some references point at nothing, and some rows are corrupt. We have seen catalog entries whose supersededBy references point at update IDs that were never published in the catalog at all, likely internal or preview identifiers, leaving you to fall back on release-date heuristics because the graph Microsoft published has dangling edges. And we have seen outright data corruption. The Malicious Software Removal Tool ships forever under one KB number (KB890830), with the version encoded only in the title and metadata; we found rows whose title version and whose download-link version disagreed, meaning the file you would download was not the version the row claimed. Worse, in our testing the generic no-architecture MSRT package is an x86 self-extracting cabinet whose own embedded version resource reads "6.1". If you installed that wrapper and then read its version back, "6.1" is numerically greater than any shipped MSRT version (the releases run on the 5.x train), so the machine would report a version that permanently masks every future MSRT update. A catalog row, taken at face value, can poison a machine's future applicability forever.
And the machine's own self-report lies too
Before we get to the answer, one more layer, because it shapes the answer. Even the machine's report about itself cannot be taken at face value.
In our own experience running feature upgrades at scale, Microsoft's upgrade installer, setup.exe, can return exit code 0, success, even when it failed during an internal initialization step; the only way to know is to go look at what it left behind in the staging directory. And after a successful in-place upgrade from Windows 10 to Windows 11, the registry ProductName value can still read "Windows 10 Pro", permanently, by design; the name the OS reports for itself cannot be trusted, so you read the build number (or the version APIs Microsoft recommends) instead. The lesson that runs through all of this: a system's self-description is a convenience, and conveniences are allowed to be stale or wrong. If you want the truth, you have to read primary state and corroborate it.
Part 2: what determines applicability
If none of the three authorities can be trusted as the answer, what do you do? You stop asking any of them for the answer, and you assemble it yourself, from primary evidence. What follows is the shape of that, the principles rather than the implementation, because the principles are the part worth understanding. The implementation underneath them is a long tail of edge cases that only matters once the shape is right.
The shape is a deliberate inversion of the normal model. Instead of asking the endpoint's local update client "what do you need," the endpoint reports raw facts about itself, and the decision about what applies is made centrally, against a catalog you control. That inversion is the whole game. It moves the applicability decision to the one place that can hold a complete, corrected picture and be fixed for the entire fleet at once when it turns out to be wrong, instead of leaving it to a black box on each machine that you cannot see into or change.
Four principles fall out of that, and each one is a direct answer to a failure from Part 1.
Read primary state instead of a tool's opinion of it. The question "what is installed on this machine" has to be answered from the place Windows physically records servicing, the component store, which reflects every servicing package regardless of how it was installed. That is the point of reading it: it sees the update that arrived by wusa.exe, by DISM, by a third-party tool, or by Windows Update all the same way, because at that level they are the same. This is the Part 1 failure (a tool can only see what flowed through its own channel) neutralized by refusing to ask a tool at all. The principle generalizes past Windows: whenever a system offers you both a self-report and the underlying state, read the state, and corroborate it from more than one independent source before you trust it.
Keep your own catalog, and own the relationships in it. You still need Microsoft's catalog, it is the only source of the updates themselves, but you cannot treat its metadata as ground truth, for every reason Part 1 laid out. So you keep your own copy, and you treat the relationships between updates, above all supersedence, as something you are responsible for getting right rather than something you inherit. The catalog's stated relationships become an input to be validated and completed before you act on them. What matters is that your picture reflects what a machine's servicing stack will do, which frequently diverges from what the published metadata says. This does not make catalog-correctness bugs disappear, we have had our own, and we have audited and fixed them across every OS version we support. What it buys you is that they become your bugs: reproducible, auditable, and fixable centrally, instead of opaque divergences buried in a client you cannot inspect.
Compute the decision centrally, and make it explain itself. With trustworthy installed state coming up from the endpoint and a corrected catalog on the server, the decision itself is a computation: this machine's observed state, plus the administrator's policy, evaluated against the catalog. The comparison is the easy part. The hard part is that a great deal of Windows servicing knowledge, which the native stack applies silently at install time, inside the black box, has to be re-encoded so the decision can be made correctly before any install is attempted, rather than discovered by the machine failing. The output is deliberately more than a yes or no: every applicable update carries the reasons it applies, so an administrator can ask "why is this being offered to this machine" and get an answer, which no native tool will give you. And it is policy-gated by default: absent an explicit policy, a machine is offered nothing, the opposite of a native scan that enumerates regardless of anyone's intent.
Treat "applicable" as a prediction, and reconcile it against reality. This is the principle the other three build toward, and it is the one most tools skip. Even a correct engine is making a prediction, and the servicing stack on the machine is the final arbiter. You can get every input right and still be wrong, because the machine holds some state you did not model. So the last move is a feedback loop: when the machine refuses, at install time, to install something you computed as applicable, and tells you so with WU_E_NOT_APPLICABLE (0x80240017), you treat that disagreement as a bug in your engine and act on it. You stop re-offering what the machine will never take, and you surface the disagreement so a human can find out why your model and reality diverged.
This is also where you stay honest. Owning the decision does not make you always right and Windows always wrong. We have had it go the other way. We were offering KB5035238, a security update for the Remote Server Administration Tools, to machines that rejected it with that error, every maintenance window, on schedule. Windows was right and we were wrong. RSAT ships as an umbrella parent feature plus a set of per-tool leaf features, and on those machines the umbrella was enabled while every patchable leaf was disabled. Our check for "is this component even installed here" matched the umbrella and concluded yes; the machine, looking at the leaves, concluded no, and the machine was correct. The only reason we found out is that we were watching for the disagreement instead of filing it as a flaky endpoint.
The whole architecture rests on one assumption: every source is wrong sometimes, including yours. So you triangulate from primary evidence, and you build the machinery to catch and correct your own divergences instead of trusting an oracle that cannot tell you when it is wrong.
What this means
Step back from the specifics and the shape is simple. "Which updates does this machine need" decomposes into four questions, and no single Windows source answers even one of them reliably on its own:
- What is installed right now? Read the primary servicing state, which sees updates from every channel.
- Which updates exist, and how do they relate? Keep your own catalog and take responsibility for the relationships, because the published metadata is incomplete and sometimes wrong.
- Given this machine's observed state and this administrator's intent, what applies, and why? Decide it centrally, with explicit reasons, before any install is attempted.
- Will it install? Only the machine can say for certain, so treat your answer as a prediction and reconcile it against what the machine does.
The Windows Update Agent answers "what did I do and what can I stage." The Settings UI answers "what did I, Windows Update, last believe." The Microsoft Update Catalog answers "what has Microsoft published, roughly related." Those are all useful questions, and all three are worth reading. None of them is "what does this specific machine need, and will the install succeed," which is the only question that matters when you are responsible for a fleet.
Applicability, done correctly, is a decision: assembled from primary evidence, kept in a form you can audit, and continuously reconciled against the one authority that cannot be argued with, the machine itself. That is a great deal more work than calling an API. It is the only version we have found that holds up, and it is how TridentStack Control decides what to patch.
