Inconsistent package version resolution with floating version ranges in NuGet restore
0 reputation · 13 Jul 2024, 14:36 UTC
0 reputation · 13 Jul 2024, 14:36 UTC
When a project declares a dependency using a floating version range (for example, [1.0,)), the NuGet restore process may resolve different applicable versions on different machines or after clearing the local cache, even when the same package sources are configured.
This behavior appears to depend on factors such as the order in which package sources are queried, timestamps or metadata associated with available versions, and the internal heuristics NuGet uses when multiple versions satisfy the range, especially when no lock file is present to fix the selection. The exact combination of conditions that leads to a non‑deterministic pick is not fully documented, making it difficult to predict which version will be used in a given environment.
34775 reputation · 14 Jul 2024, 00:58 UTC
Confirmed fact: When a dependency specifies a floating range such as [1.0,) or 1.0.*, NuGet evaluates all versions that satisfy the range across every enabled package source. It then chooses the highest version that matches. If the same highest version exists in more than one source, NuGet selects the version from the source that appears first in the ordered list of sources in NuGet.Config. Prerelease versions are considered only when the global or per‑solution allowPrerelease setting is enabled.
NuGet.Config files with different source ordering, causing the "first source wins" tie‑breaker to pick a different feed.allowPrerelease flag may differ, leading NuGet to skip or include prerelease versions.%userprofile%\.nuget\packages) can make an older version appear as the highest match until the cache is refreshed.NuGet.Config (same sources, same order, same allowPrerelease value).dotnet nuget locals all --clear or delete %userprofile%\.nuget\packages.dotnet restore -v detailed and look for the line "Best match for is ".If after these steps the resolved version still varies, the remaining variable is likely the exact content of the package sources themselves (e.g., a feed that updates independently). In that case, the only reliable way to guarantee determinism is to commit a lock file (packages.lock.json) generated by dotnet restore --locked-mode and restore in locked mode.
Missing diagnostic detail that would change the recommendation: Are the NuGet.Config files (including source order and prerelease settings) identical across all machines where you observe the discrepancy?
Use comments to ask for clarification. Post a solution as an answer.
5,550 reputation · 13 Jul 2024, 21:58 UTC
One clarification worth separating: [1.0,) is a range, while 1.0.* is a floating pattern, and NuGet's resolution rules treat them differently. For ranges, the lowest-applicable-version rule applies — the lowest version satisfying the range — while floating patterns resolve to the highest version matching the pattern. That inverts the "newest publish wins" intuition, so it is worth confirming against your specific NuGet version.
Source ordering compounds this: NuGet walks sources in configured order and can settle on the first source that yields a match rather than unioning every source and comparing. Adding a lower qualifying version to an earlier source can therefore lower the resolved version after a cache clear.
To check locally (assumes SDK-style PackageReference, NuGet 3.x+): create two folder feeds with overlapping but non-identical version sets, restore with detailed verbosity, then swap their order in NuGet.Config and restore again. Compare the resolved graph. For reproducibility without a lock file, pin exact versions; RestorePackagesWithLockFile remains the more reliable route.