.NET 10 NU1015 turns a PackageReference without a version into a restore error. I like the stricter default because an unbounded direct dependency can quietly resolve the lowest package version. The catch is that versionless XML is also the correct shape for NuGet Central Package Management (CPM). A mechanical “add Version everywhere” repair can undo the policy your repository intended to enforce.
I use a simple split: first decide who owns the version, then make restore prove the answer.
Why .NET 10 NU1015 stops the build
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. Starting with .NET 10, the same mistake produces NU1015 and restore fails. Microsoft documents this as a stable behavioral change in the .NET 10 compatibility guidance.
Here is the ambiguous project entry:
<ItemGroup>
<PackageReference Include="Demo.Greeting" />
</ItemGroup>
If this is a normal direct reference, the project is missing its version. If CPM is active, the project is correct and the version should live elsewhere. 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.
That distinction matters more than silencing the error. It tells me whether the project file or the repository-level package policy is broken.
The timing can be misleading. 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. I inspect the failing project's evaluated inputs, nearby props files, and recent path changes before editing package metadata. That keeps a restore migration from turning into an accidental package-management migration.
Fix the owner, not only the XML
For a direct reference, I add an explicit version:
<PackageReference Include="Demo.Greeting" Version="2.0.0" />
For CPM, I keep the project reference versionless and put the version in the nearest Directory.Packages.props:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Demo.Greeting" Version="2.0.0" />
</ItemGroup>
</Project>
NuGet automatically imports only the first Directory.Packages.props it finds while walking up from a project. A nested file can therefore change which policy applies, and a copied project can lose the import completely. The official Central Package Management guide describes both the enabling property and the nearest-file rule.
I check the evaluated project context instead of assuming that a props file somewhere in the repository is active. The two valid outcomes are clear: a direct PackageReference owns its Version, or an active CPM file owns the matching PackageVersion.
Reproduce NU1015 without an external feed
The runnable sample creates a local package feed containing Demo.Greeting versions 1.0.0 and 2.0.0. It then tests three consumers:
-
BrokenDirecthas no version and must fail withNU1015. -
FixedDirectpins2.0.0on the reference. -
CentralManagedleaves the reference versionless and pins2.0.0inDirectory.Packages.props.
From the sample folder, I run:
dotnet restore .\Verifier\Verifier.csproj
dotnet build .\Verifier\Verifier.csproj -c Release --no-restore
dotnet run --project .\Verifier\Verifier.csproj -c Release --no-build --no-restore
The verifier packs both fixture versions, runs each restore, reads project.assets.json, and executes the repaired projects. The important assertions are not just “restore passed.” Both supported repairs must resolve and run with 2.0.0.
The sample also demonstrates the documented compatibility switch:
dotnet restore .\BrokenDirect\BrokenDirect.csproj `
-p:SdkAnalysisLevel=9.0.300 `
-p:TreatWarningsAsErrors=false
That brings back NU1604, and the local feed proves why I do not treat it as the fix: NuGet chooses 1.0.0, the lowest available version. The complete validation and review history is in the merged pull request.
Limits and when I avoid the escape hatch
SdkAnalysisLevel=9.0.300 affects every SDK behavior gated by that level, not only this diagnostic. A warnings-as-errors policy can also promote NU1604 back to a failure. I reserve the switch for short migration diagnostics while I determine who should own the version.
The local-feed sample does not model authenticated sources, source mapping, package lock files, or feed outages. Those controls still matter in a production restore. Its narrower job is to make the ownership decision and resolved version deterministic.
I also would not add Version="0.0.0" merely to make NU1015 disappear. Microsoft documents that value for the unusual case where the lowest version is genuinely intended, but it still produces NU1603 when NuGet resolves a higher available version. Most application dependencies need a deliberate version or an active central policy instead.
For direct references, I pin the intended dependency. For CPM repositories, I verify the import boundary and keep versions centralized. Either way, I leave restore with one explicit owner and a test that checks the resolved graph.
Have you hit NU1015 because a direct version was missing, or because a project silently lost its CPM context?
Happy coding!
Top comments (0)