Dependency Resolution
|
This section documents MSBuild 18.9, the .NET SDK 10.0.402 (with its This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production. The C# Reference bibliography lists the reference material consulted while preparing these pages. |
A project lists a few packages; those packages depend on others; and in the end every package ID must have exactly one version in the restored graph. NuGet’s resolver picks that version with four rules and reports, with NU codes, when the graph is not what the rules would like. This page reproduces each case with NuGet 7.9.0 (.NET SDK 10.0.402) and six small packages created for the purpose:
| Package | Depends on |
|---|---|
|
— |
|
|
|
|
|
|
|
|
|
|
|
— (only a |
Each scenario is a scratch project, samples/Deps/Deps.csproj in a copy of Ledgerline, with only the
PackageReference items changing; it opts out of the repository’s lock file and warnings-as-errors so that each
restore shows its raw result.
Source: Dependency resolution: Rules.
Rule 1: lowest applicable version
NuGet picks the lowest version that satisfies a range, not the highest. Ledgerline.Rates asks for
Ledgerline.Money >= 1.2.0; the feed has no 1.2.0, so the lowest version above it, 1.5.0, is chosen — with a
warning, because the package author’s minimum was never tested:
~/ledgerline/samples/Deps/Deps.csproj : warning NU1603: Ledgerline.Rates 1.0.0 depends on Ledgerline.Money (>= 1.2.0) but Ledgerline.Money 1.2.0 was not found. Ledgerline.Money 1.5.0 was resolved instead.
resolved: Ledgerline.Money/1.5.0, Ledgerline.Rates/1.0.0
Picking the lowest keeps restores stable: publishing Money 3.0.0 tomorrow does not change today’s graph.
Source: Lowest applicable version.
Rule 2: floating versions
A floating version (1.*, *-*) is the exception: it resolves to the highest match
(Package Versions and Ranges).
Source: Floating versions.
Rule 3: direct dependency wins
A version the project references directly wins over any transitive request — even a lower one. With Ledgerline.Fx
(needs Money >= 2.0.0) and a direct Ledgerline.Money 1.0.0:
~/ledgerline/samples/Deps/Deps.csproj : error NU1605: Warning As Error: Detected package downgrade: Ledgerline.Money from 2.0.0 to 1.0.0. Reference the package directly from the project to select a different version.
~/ledgerline/samples/Deps/Deps.csproj : error NU1605: Deps -> Ledgerline.Fx 1.0.0 -> Ledgerline.Money (>= 2.0.0)
~/ledgerline/samples/Deps/Deps.csproj : error NU1605: Deps -> Ledgerline.Money (>= 1.0.0)
resolved: Ledgerline.Fx/1.0.0, Ledgerline.Money/1.0.0
The direct 1.0.0 won, and the SDK reports the downgrade as an error — NU1605 is in the SDK’s default
WarningsAsErrors even though TreatWarningsAsErrors is off — because Ledgerline.Fx may call APIs that 1.0.0 does
not have. Raise the direct reference (or remove it). Direct references are also how a consumer upgrades a
transitive package, for example to a version with a security fix.
Source: Direct dependency wins.
Rule 4: cousin dependencies
When two packages need different ranges of the same package, NuGet picks the lowest version that satisfies all of
them. Ledgerline.Fx needs >= 2.0.0 and Ledgerline.Rates >= 1.2.0:
resolved: Ledgerline.Fx/1.0.0, Ledgerline.Money/2.0.0, Ledgerline.Rates/1.0.0
No warning: 2.0.0 is inside both ranges. dotnet nuget why shows why a transitive package is there:
dotnet nuget why Deps.csproj Ledgerline.Money
Project 'Deps' has the following dependency graph(s) for 'Ledgerline.Money':
[net10.0]
├── Ledgerline.Fx (v1.0.0)
│ └── Ledgerline.Money (v2.0.0)
└── Ledgerline.Rates (v1.0.0)
└── Ledgerline.Money (v2.0.0)
With NuGet 7.9.0 the command printed nothing, with exit code 0, when its output was redirected to a file or a
pipe; the tree above was captured through a pseudo-terminal (script -qc "dotnet nuget why …" /dev/null). In CI logs,
use dotnet package list --include-transitive instead, which does print when redirected (but does not show the
paths).
Source: Cousin dependencies, dotnet nuget why.
When the ranges do not meet
Ledgerline.Fx 1.0.0 needs >= 2.0.0, Ledgerline.Tax needs [1.0.0,2.0.0) — no version satisfies both. The
resolver chose 2.0.0 and warned:
~/ledgerline/samples/Deps/Deps.csproj : warning NU1608: Detected package version outside of dependency constraint: Ledgerline.Tax 1.0.0 requires Ledgerline.Money (>= 1.0.0 && < 2.0.0) but version Ledgerline.Money 2.0.0 was resolved.
Even two exact requirements — Ledgerline.Fx 2.0.0 needs [2.0.0], Ledgerline.Audit needs [1.0.0] — gave
only NU1608 with the default resolver:
~/ledgerline/samples/Deps/Deps.csproj : warning NU1608: Detected package version outside of dependency constraint: Ledgerline.Audit 1.0.0 requires Ledgerline.Money (= 1.0.0) but version Ledgerline.Money 2.0.0 was resolved.
The same restore with the legacy resolver (-p:RestoreUseLegacyDependencyResolver=true) failed with the
documented version-conflict error:
~/ledgerline/samples/Deps/Deps.csproj : error NU1107: Version conflict detected for Ledgerline.Money. Install/reference Ledgerline.Money 2.0.0 directly to project Deps to resolve this issue.
~/ledgerline/samples/Deps/Deps.csproj : error NU1107: Deps -> Ledgerline.Audit 1.0.0 -> Ledgerline.Money (= 1.0.0).
~/ledgerline/samples/Deps/Deps.csproj : error NU1107: Deps -> Ledgerline.Fx 2.0.0 -> Ledgerline.Money (= 2.0.0)
NuGet rewrote the resolver for performance in 6.12 (.NET 9) and documents the new one as "functionally equivalent"; here it was not: the old one stopped with an error, the new one restored with a warning. Treat NU1608 as seriously as NU1107 — one package is now running against a dependency version its author excluded. The fixes are the same: reference the dependency directly at a version both packages accept, upgrade the package with the narrow range, or drop one of them.
Incompatible frameworks: NU1701
Ledgerline.Legacy contains only lib/net48/. A net10.0 project still restores it, through the default
AssetTargetFallback, and warns:
~/ledgerline/samples/Deps/Deps.csproj : warning NU1701: Package 'Ledgerline.Legacy 1.0.0' was restored using '.NETFramework,Version=v4.6.1, .NETFramework,Version=v4.6.2, .NETFramework,Version=v4.7, .NETFramework,Version=v4.7.1, .NETFramework,Version=v4.7.2, .NETFramework,Version=v4.8, .NETFramework,Version=v4.8.1' instead of the project target framework 'net10.0'. This package may not be fully compatible with your project.
The library is .NET Framework code: it may load and work, or fail at run time when it touches an API .NET does not
have. Look for a version of the package with a netstandard2.0 or net8.0+ build.
Source: AssetTargetFallback.
Prereleases and assets
A range only matches a prerelease when the range itself contains a prerelease bound (2.1.0-*, >= 2.1.0-beta.0) or
floats with *-*; a dependency on >= 2.0.0 never selects 2.1.0-beta.1. Which parts of a resolved package flow
to the project and to its consumers is controlled separately with IncludeAssets, ExcludeAssets and
PrivateAssets (PackageReference).
Source: Managing dependency assets.
Common mistakes
-
Ignoring NU1603 — the dependency was never tested at the version you got.
-
Suppressing NU1605 — a downgrade breaks the package that needed the newer version.
-
Treating NU1608 as harmless — with the 6.12 resolver it replaces some NU1107 errors.
-
Exact versions in package dependencies — they create conflicts for every consumer.
-
dotnet nuget whyin CI scripts — empty output when redirected (NuGet 7.9.0).