.NET 10 fails restore on a versionless PackageReference, and the reflexive fix breaks CPM
NU1015 replaces a warning that used to resolve the lowest version on the feed. The same markup is correct under Central Package Management, so the error names a symptom, not an owner.
The Engineer · Build desk
What happened
- In .NET 10, a PackageReference with no Version now fails restore outright as error NU1015.
- Earlier SDKs reported the same mistake as warning NU1604 and let restore continue by selecting the lowest version available on the configured sources.
- That same versionless markup is the correct form under Central Package Management, so the error text does not identify which side is actually broken.
- In the article's sample, reverting via SdkAnalysisLevel=9.0.300 restores the warning and resolves 1.0.0 from a feed that also holds 2.0.0.
Why it matters
- decision Every failing project now forces someone to name an owner for the version before editing anything, and adding Version to the reference answers that question wrong wherever CPM was the intent.
- exposure Repositories that were correct all along fail on an unrelated SDK bump, and the mechanical repair leaves central management switched on while each project quietly decides its own version again.
- cost The documented escape hatch is paid for twice: it unblocks restore at the lowest version on the feed, and it drags every other SDK behaviour gated at that analysis level back with it.
- constraint A green restore stops being evidence of a correct repair, so verification has to read the resolved version out of the assets file rather than trust an exit code.
The diagnostic cannot tell two opposite faults apart, and that is the part worth engineering around. A missing version and a correctly centralised version are the same XML [3]. Restore emits the same NU1015 whether a developer forgot to pin a package or a repository stopped importing the file that pins it, and the NU1015 documentation names the second case directly: a project that expected Central Package Management ended up somewhere CPM is off or its props file is no longer found [4].
The discovery rule is what makes that failure mode easy to hit. NuGet imports only the first `Directory.Packages.props` it encounters while walking up from the project, so a nested props file added for one subtree changes which policy governs everything under it, and a project moved to a new path can lose the import outright [6]. Neither event edits a single project file. That is why the dev.to walkthrough checks evaluated project inputs, neighbouring props files, and recent path changes before touching any package metadata [7]. The timing of the failure is a poor witness: an SDK upgrade surfaces an old direct reference that had always coasted on lowest-version resolution, while a repository reorganisation breaks a CPM import that was valid the day before [8].
The sample feed is where the old behaviour gets priced. It carries Demo.Greeting 1.0.0 and 2.0.0 [9]. Both supported repairs, a pinned `Version` on the reference and a `PackageVersion` in the nearest central file, must resolve and run 2.0.0 [10]. Restore the broken project with `-p:SdkAnalysisLevel=9.0.300 -p:TreatWarningsAsErrors=false` and NU1604 returns, and NuGet picks 1.0.0, the lowest available [11]. A full major version separates the lenient path from the repaired one on a two-package feed [17], which is a fair account of what the warning was permitting in repositories nobody was watching. The author reserves that switch for short migration diagnostics, since it moves every SDK behaviour gated at 9.0.300 along with the one you wanted [12].
The test discipline in the sample is the transferable part. The verifier packs both fixture versions, runs each restore, reads `project.assets.json`, and executes the repaired projects, asserting the resolved version rather than a successful restore [10]. A pasted version and a working CPM import both produce a green restore; only the resolved version tells you which one you shipped.
Two caveats sit on the edges. Repositories that already treat warnings as errors never had the lenient path to begin with, because that policy could promote NU1604 to a failure on its own [13]. And the local-feed sample deliberately skips authenticated sources, source mapping, package lock files, and feed outages [14], all of which sit between a passing fixture and a production restore.
What to watch
- Whether repositories that pasted versions to clear NU1015 leave ManagePackageVersionsCentrally set to true, which would keep the property on while the central file governs nothing.
- Whether Microsoft keeps SdkAnalysisLevel=9.0.300 honouring the old NU1604 behaviour across later 10.x SDK patches.
- How the failure behaves with the controls the sample omits, in particular package lock files and source mapping on authenticated feeds.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence66
- Adoption25
- Hype gap−5
- Incentives30
- Confidence58
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
.NET 10 turns a PackageReference without a version into a restore error, NU1015.
- [2]
Before .NET 10, NuGet reported NU1604 when a direct reference had no inclusive lower bound; restore could continue and select the lowest version available from the configured sources.
- [3]
Versionless PackageReference XML is also the correct shape for NuGet Central Package Management, so identical markup is either a missing version or a correct CPM reference depending on whether CPM is active.
- [4]
The NU1015 diagnostic reference calls out a common failure mode: a project that expected CPM was copied into a location where CPM is disabled or its props file is no longer discovered.
- [5]
Microsoft documents the NU1604-to-NU1015 change as a stable behavioral change in the .NET 10 compatibility guidance.
- [6]
NuGet automatically imports only the first Directory.Packages.props it finds while walking up from a project, so a nested file can change which policy applies and a copied project can lose the import completely.
- [7]
The author inspects the failing project's evaluated inputs, nearby props files, and recent path changes before editing package metadata, to keep a restore migration from becoming an accidental package-management migration.
- [8]
An SDK upgrade may expose an old direct reference that had always relied on lowest-version resolution, while a repository move may break a previously valid CPM import.
- [9]
The runnable sample creates a local package feed containing Demo.Greeting 1.0.0 and 2.0.0, and tests three consumers: BrokenDirect with no version that must fail with NU1015, FixedDirect pinning 2.0.0 on the reference, and CentralManaged leaving the reference versionless with 2.0.0 pinned in Directory.Packages.props.
- [10]
The verifier packs both fixture versions, runs each restore, reads project.assets.json and executes the repaired projects; the important assertion is not that restore passed but that both supported repairs resolve and run with 2.0.0.
- [11]
Restoring the broken project with -p:SdkAnalysisLevel=9.0.300 -p:TreatWarningsAsErrors=false brings back NU1604, and against the local feed NuGet chooses 1.0.0, the lowest available version.
- [12]
SdkAnalysisLevel=9.0.300 affects every SDK behavior gated by that level, not only this diagnostic, and the author reserves the switch for short migration diagnostics while deciding who should own the version.
- [13]
A warnings-as-errors policy can also promote NU1604 back to a failure.
- [14]
The local-feed sample does not model authenticated sources, source mapping, package lock files, or feed outages, which still matter in a production restore.
- [15]
The author states two valid outcomes: a direct PackageReference owns its Version, or an active CPM file owns the matching PackageVersion.
- [16]
CPM is enabled by setting ManagePackageVersionsCentrally to true in Directory.Packages.props and declaring PackageVersion items there.
- [17]
On the sample feed the reverted, lenient restore lands a full major version below the 2.0.0 that both supported repairs must resolve.
Get this ledger inside ChatGPT. Ask about any of our stories and the answer brings its figure, each claim’s verdict and the sources. Free, no account.
Sources
1 independent publisher whose own reporting we read for this story.
- dev.to.NET 10 NU1015: Fix PackageReference Without Version Restore Failures
1 article · August 23, 2026
Topics and entities
Follow any of these and your For You feed starts watching them — no settings page required.