Safeguard
Developer Tools•

Why does NuGet pick the lowest package version, and is it safe?

NuGet resolves to the lowest version that satisfies every constraint. That keeps builds predictable, but it can leave a vulnerable version in place.

Safeguard Security Team
3 min read

NuGet restores the lowest version that satisfies all constraints, so a dependency declared as 1.0 or higher resolves to 1.0, not the newest release. This makes builds predictable, but it also means an old version with a known flaw can stay in your graph until you raise it.

The rule, in plain terms

Microsoft's dependency resolution documentation describes four rules for PackageReference projects: lowest applicable version, floating versions, direct dependency wins, and cousin dependencies. The first one matters most for security.

A plain version number such as 6.1 means "6.1 or higher", and NuGet resolves to the smallest acceptable stable version. It does not hunt for the newest.

Examples from the docs

DeclaredMeaningResult
Version 6.16.1 or aboveLowest acceptable stable version
Version 6.*Any 6.xHighest matching stable (floating)
Exact 6.1.0 in square bracketsExactly 6.1.06.1.0

If the requested minimum is not on the feed, NuGet picks the next lowest version it can find, so a request for 2.1 or higher may resolve to 2.2.

Why this is good for repeatability

Because the answer does not drift upward each time a new release appears, builds are stable. The same inputs give the same graph, unless a lower matching version is published later. Microsoft notes that case too: publishing version 4.0.0 after you declared 4.0.0 or higher can change what resolves, because the exact match now exists.

Why it can leave you exposed

Package authors often declare a loose lower bound for compatibility. If package A says it needs B at 1.0 or higher, you get B 1.0, even if 1.0 has an advisory. Nothing upgrades it for you.

Two other rules affect what you can do:

  • Direct dependency wins. If you reference B yourself, your version is used for that subgraph. This lets an application override a transitive version, and NuGet warns on a downgrade.
  • Cousin dependencies. When different parts of the graph ask for different versions, NuGet picks the lowest version that satisfies all of them.

What to do about it

  1. Run an audit with transitive checking on, so low bounds do not hide advisories.
  2. Raise the closest package to a version whose own dependency range excludes the bad version.
  3. If that does not help, add a direct reference or use central package management with transitive pinning.
  4. Use a lock file so the result stays fixed once you have fixed it.
  5. Remove temporary direct references when the parent package catches up.

Try it yourself

Click through it at your own pace, or open it full screen. Find a transitive package and note which parent brought it in.

Safeguard's dependency scanning shows resolved versions across the graph, and a free scan lists any with known advisories.

Quick check

Will NuGet automatically upgrade a transitive package to the newest release?

No. It uses the lowest version that satisfies all constraints unless you reference or pin something higher.

What happens if I reference a transitive package directly at a lower version?

NuGet honors your direct reference and raises a downgrade warning, so do not go lower than your dependencies ask for.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.