DOCS

winget upgrade shows an Unknown version

winget list reports the installed version as Unknown, and upgrade logic either does nothing useful or tries to reinstall the application every run.

winget list prints Unknown in the version column, and from there everything downstream gets strange: upgrades that do not fire, or worse, a package that reinstalls itself on every reconcile.

Where Unknown comes from

Winget determines installed versions largely from Add/Remove Programs entries in the registry. Plenty of applications simply do not write a usable version there — the value is missing, or is not version-shaped, or the entry was written by an installer that had other ideas. Portable and archive-based installs frequently have no meaningful version at all.

When we read ARP ourselves and find an entry with no display version, we stamp it explicitly as Unknown rather than inventing something. Similarly, when parsing winget’s own output, we take the first genuinely version-shaped token from the version column and otherwise emit Unknown — never a best-effort guess at the first token available. A shifted column could otherwise let a package id or a bare date survive as a “version” and drive a completely spurious upgrade.

The destructive failure this causes

Here is why it matters more than a cosmetic gap.

Any sane version comparator orders Unknown below every real version number. So a naive reconciler sees installed Unknown against a target of, say, 12.4.1, concludes the machine is behind, and worse — because the two look like different major versions — may decide the safe path is to uninstall and reinstall.

Now do that every cycle. You get a package that is torn down and rebuilt continuously, with real risk of application loss whenever the reinstall leg fails and the uninstall leg already succeeded.

Refusing to act is the correct answer

Our reconciler has an explicit rule: an install intent against an Unknown observed version is converged as present. No step is emitted, in any intent mode.

That is a deliberate refusal rather than an oversight. We cannot know whether the machine is ahead, behind, or exactly right, and every action available is worse than inaction. The package is present; presence is what we can honestly assert; we stop there.

The same discipline runs server-side. Coordinates are excluded from update-only targeting when the version is empty or Unknown, when the same id has been observed at several different versions and is therefore ambiguous, when the id is registry-path-shaped rather than a real package id, and when the observation is too recent to be trusted.

Getting out of Unknown

The escape hatch is identity that does not depend on a version string at all.

Where a package has a collision-resistant product code — an MSI GUID, or the installer-generated variant — an existing install can be correlated by that instead, and the coordinate converges as a no-op without anyone having to know what version it is. This is the difference between our recognition tiers: a winget-authoritative match, a product-code match, and a name-and-version heuristic that is treated as the weakest of the three.

Presence inferred purely from a display-name match is handled most conservatively of all. It carries no product code, no canonical id, and its version is always the Unknown sentinel — and both uninstall paths explicitly refuse to act on it. That refusal is permanent by design, not a deferral: we will happily tell you the software is there, and we will not let anything remove it on that basis alone.