From 60fef0fdbe1ad908a4f5432d4a3ee6dc4d7ab186 Mon Sep 17 00:00:00 2001 From: GabrielDuf Date: Thu, 22 Oct 2026 13:49:22 -0400 Subject: [PATCH] Drop scope and architecture when retrying WinGet with the local identifier When WinGet answers an update or uninstall with 0x8A150014 ("No installed package found matching input criteria"), the #5413 fallback retries with the installed entry's local identifier (for example an ARP\Machine\X86\ {...}_is1 key). The retry still passed --scope and --architecture from the user's or package's install options, and WinGet applies --scope as a filter on the installed-package query: `winget list --id --exact --scope user` reports no match for a machine-wide entry. So a machine-wide x86 install with a user-scope/x64 default was filtered out again, failed with the same error, and since the flag was already set it never retried. The manual-update command shared the same parameters and printed the same broken command line. The local identifier already names one exact installed entry, and its ARP path encodes that entry's own scope and architecture, so any constraint can only hide it or disagree with it. GetEffectiveScope and GetEffectiveArchitecture now return null whenever the selector actually uses a resolved local identifier, which is tied to the same check GetSelectorPiece uses so the two cannot diverge. If the local id cannot be resolved the catalog id is used and the user's constraints are kept. The first attempt with the catalog id is unchanged. Fixes #5522 Co-Authored-By: Claude Opus 5.5 --- .../Helpers/WinGetPkgOperationHelper.cs | 16 +++--- .../WinGetManagerTests.cs | 52 +++++++++++++++++++ 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs b/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs index 44080f66b6..0a03038542 100644 --- a/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs +++ b/src/UniGetUI.PackageEngine.Managers.WinGet/Helpers/WinGetPkgOperationHelper.cs @@ -28,15 +28,19 @@ public static string GetIdNamePiece(IPackage package) private static string GetSelectorPiece(IPackage package) { - if ( - package.OverridenOptions.WinGet_UseLocalIdentifier - && GetFallbackIdentifier(package) is { } localIdentifier - ) + if (GetSelectedLocalIdentifier(package) is { } localIdentifier) return $"--id {Selector(localIdentifier, "local identifier")} --exact"; return GetIdNamePiece(package); } + private static string? GetSelectedLocalIdentifier(IPackage package) => + package.OverridenOptions.WinGet_UseLocalIdentifier ? GetFallbackIdentifier(package) : null; + + private static bool DropsArchAndScope(IPackage package) => + package.OverridenOptions.WinGet_DropArchAndScope + || GetSelectedLocalIdentifier(package) is not null; + private static bool OperationIsBrokered(IPackage package) => Settings.Get(Settings.K.UseAgentBroker) && !package.Source.IsVirtualManager; @@ -66,7 +70,7 @@ public WinGetPkgOperationHelper(WinGet manager) internal static string? GetEffectiveScope(IPackage package, InstallOptions options) { - if (package.OverridenOptions.WinGet_DropArchAndScope) + if (DropsArchAndScope(package)) return null; return (package.OverridenOptions.Scope ?? options.InstallationScope) switch @@ -94,7 +98,7 @@ public WinGetPkgOperationHelper(WinGet manager) bool forUpdate = false ) { - if (package.OverridenOptions.WinGet_DropArchAndScope) + if (DropsArchAndScope(package)) return null; string architecture = diff --git a/src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs b/src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs index 96cfe3d1d3..29c113224f 100644 --- a/src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs +++ b/src/UniGetUI.PackageEngine.Tests/WinGetManagerTests.cs @@ -1929,6 +1929,58 @@ public void WinGetUpdateRetriesWithTheLocalIdentifierWhenNoInstalledPackageMatch Assert.Contains("--exact", retryAttempt); } + [Fact] + public void WinGetLocalIdentifierRetryDropsTheScopeAndArchitectureThatHidTheInstalledEntry() + { + const string fanControlLocalIdentifier = + @"ARP\Machine\X86\{141A88F8-31AC-49EA-B428-2BE8C19DED83}_is1"; + var manager = new WinGet(); + SetCliToolKind(manager, WinGetCliToolKind.SystemWinGet); + var package = new PackageBuilder() + .WithManager(manager) + .WithId("Rem0o.FanControl") + .WithVersion("251") + .WithNewVersion("268") + .Build(); + NativePackageHandler.AddLocalIdentifier(package, fanControlLocalIdentifier); + static InstallOptions UserScopeX64() => + new() { InstallationScope = PackageScope.User, Architecture = Architecture.x64 }; + + var firstAttempt = manager.OperationHelper.GetParameters( + package, + UserScopeX64(), + OperationType.Update + ); + Assert.Contains("--scope", firstAttempt); + Assert.Contains("--architecture", firstAttempt); + + var veredict = manager.OperationHelper.GetResult( + package, + OperationType.Update, + [], + unchecked((int)0x8A150014) + ); + OperationAssert.HasVeredict(veredict, OperationVeredict.AutoRetry); + + var retryAttempt = manager.OperationHelper.GetParameters( + package, + UserScopeX64(), + OperationType.Update + ); + Assert.Contains($"\"{fanControlLocalIdentifier}\"", retryAttempt); + Assert.DoesNotContain("--scope", retryAttempt); + Assert.DoesNotContain("--architecture", retryAttempt); + + var manualCommand = manager.OperationHelper.GetStandaloneParameters( + package, + UserScopeX64(), + OperationType.Update + ); + Assert.Contains($"\"{fanControlLocalIdentifier}\"", manualCommand); + Assert.DoesNotContain("--scope", manualCommand); + Assert.DoesNotContain("--architecture", manualCommand); + } + [Fact] public void WinGetUninstallRetriesWithTheLocalIdentifierWhenNoInstalledPackageMatches() {