dotnet package add
Add, list, audit and remove NuGet packages from the command line, with pinned versions and Central Package Management.
dotnet package add <PACKAGE_ID> [--version <version>] [--project <path>] [--prerelease] [--source <url>] [--no-restore] [-f <tfm>]- Added in .NET .NET 10 SDK (noun-first form); dotnet add package since .NET Core 1.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$PACKAGE_ID | string | NuGet package id, e.g. Npgsql.EntityFrameworkCore.PostgreSQL. PACKAGE_ID@VERSION also works. |
$--version, -voptional | string= the latest stable version on the feed; if it does not support the project's target framework the command fails and leaves the project unchanged | Exact version to reference. |
$--projectoptional | path= the project in the current folder | Project to change. |
$--prereleaseoptional | flag= false | Allow a prerelease version when no --version is given. |
$--source, -soptional | url= the sources in nuget.config | Feed to query during this command, in addition to (not instead of) restore's normal sources. |
$--no-restoreoptional | flag= false | Only edit the project file; do not restore. |
$--framework, -foptional | TFM= all target frameworks | Add the reference only for one target framework. |
Return value
process exit code0 when the reference was added and restored. Non-zero when the package or version does not exist, is incompatible with the target framework, or restore fails.
Errors
Fails with NU1101 when the package id is not found on any source and NU1102 when the requested version does not exist. Vulnerable packages produce NU1901 to NU1904 warnings during restore.
Changelog
- PHP .NET 9NuGet audit reports vulnerabilities in transitive packages by default.
- PHP .NET 10Noun-first commands: dotnet package add, dotnet package list, dotnet package remove. The verb-first forms keep working.
dotnet package add edits the project file to add a <PackageReference> and then restores, so a typo or an incompatible version fails immediately. It is the noun-first spelling of dotnet add package; both work, and the same family has dotnet package list, dotnet package remove and dotnet package search.
Always say the version
Without --version the command takes the latest stable version on the feed; if that version does not support the target framework of the project the command fails (NU1202) and leaves the project unchanged rather than falling back to an older one. That is fine on day one and wrong in a script: run it next month and you get a new major version with breaking changes. Write --version 10.0.3 (or [email protected]) in docs and scripts, and keep the packages that ship with the runtime, ASP.NET Core (Microsoft.AspNetCore.*) and EF Core (Microsoft.EntityFrameworkCore.*), on the same patch, 10.0.13 here. Out-of-band packages such as Microsoft.Extensions.Http.Resilience (10.11.0) have their own version line.
With Central Package Management
When Directory.Packages.props sets ManagePackageVersionsCentrally to true, versions live in <PackageVersion> items there and project files carry only <PackageReference Include="..." />. dotnet package add follows that layout: it writes the versionless reference to the project and adds a central PackageVersion when one does not exist. A hand-edited Version attribute on a reference in a CPM repository is a build error (NU1008), which is the point: one version per package for the whole solution.
Auditing what you have
| Command | Answers |
|---|---|
dotnet package list | Requested and resolved versions per project |
dotnet package list --outdated | Newer versions on the feed |
dotnet package list --vulnerable --include-transitive | Known advisories, including packages you never referenced directly |
dotnet package list --deprecated | Packages their owners have deprecated |
NuGet audit also runs on every restore and reports NU1901 (low) to NU1904 (critical); since .NET 9 it checks transitive packages by default. Treat NU1903 and NU1904 as errors in the main-branch and scheduled audit builds (pull requests keep them as warnings; see CI Quality Gates). The fix for a vulnerable transitive package is to upgrade the direct dependency or pin a fixed version centrally.
Feeds and supply chain
--source adds a feed for that one command; restore later uses nuget.config only, so a package found that way fails on CI. Put internal feeds in nuget.config with packageSourceMapping, so Contoso.* can only come from the internal feed and nobody can publish a look-alike on NuGet.org that wins (dependency confusion). Commit packages.lock.json and restore with --locked-mode in CI so a resolved version can never change without a commit.
Example
# Add packages to the API with exact versions (CPM puts them in Directory.Packages.props).
dotnet package add Npgsql.EntityFrameworkCore.PostgreSQL --version 10.0.3 --project src/Orders.Api
dotnet package add Microsoft.Extensions.Http.Resilience --version 10.11.0 --project src/Orders.Api
dotnet package add [email protected] --project src/Orders.Api
# The verb-first spelling still works and does the same thing.
dotnet add src/Orders.Api package Asp.Versioning.Http --version 10.2.3
# Test-only packages go to the test project.
dotnet package add Testcontainers.PostgreSql --version 4.16.0 --project tests/Orders.IntegrationTests
# What do we depend on, what is stale, what is vulnerable?
dotnet package list --project src/Orders.Api --include-transitive
dotnet package list --outdated
dotnet package list --vulnerable --include-transitive
# Find a package id without leaving the terminal.
dotnet package search HealthChecks.NpgSql --take 5
dotnet package remove Swashbuckle.AspNetCore --project src/Orders.ApiWhen to use it
- Add Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3 to Orders.Api in a repository that uses Central Package Management
- Fail the main-branch and scheduled audit builds when dotnet restore reports a high or critical advisory (NU1903, NU1904) in any transitive package
- List outdated packages across the solution before a monthly dependency-upgrade pull request
Frequently asked questions
Is dotnet package add different from dotnet add package?
No. dotnet package add is the noun-first form added in the .NET 10 SDK; dotnet add package still works and does the same thing.
Why do I get NU1008 after adding a package?
The repository uses Central Package Management and a PackageReference has a Version attribute. Remove the attribute from the project and put the version in Directory.Packages.props.
How do I fix a vulnerable transitive package?
Upgrade the direct dependency that brings it in. If no fixed version exists yet, add a PackageVersion for the fixed transitive version with CentralPackageTransitivePinningEnabled, or a direct PackageReference without CPM.
More examples
Directory.Packages.props after a few adds
One version per package for every project. With transitive pinning on, a <code>PackageVersion</code> entry also overrides the version of that package when it arrives transitively, which is how a vulnerable transitive dependency is patched before its parent ships a fix.
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.3" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.13" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="10.0.13" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.11.0" />
<PackageVersion Include="Asp.Versioning.Http" Version="10.2.3" />
<PackageVersion Include="Testcontainers.PostgreSql" Version="4.16.0" />
<PackageVersion Include="xunit.v3" Version="4.0.2" />
</ItemGroup>
</Project>nuget.config with source mapping
<code><clear /></code> drops machine-level feeds so every developer and CI agent restores from the same list. The mapping guarantees <code>Contoso.*</code> packages come only from the internal feed, closing the dependency-confusion hole.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<packageSources>
<clear />
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
<add key="contoso" value="https://pkgs.contoso.example/nuget/v3/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="contoso">
<package pattern="Contoso.*" />
</packageSource>
<packageSource key="nuget.org">
<package pattern="*" />
</packageSource>
</packageSourceMapping>
</configuration>Audit gate in the project defaults
Placed in <code>Directory.Build.props</code>. <code>NuGetAuditMode=all</code> covers transitive packages, <code>NuGetAuditLevel</code> hides low-severity noise, the <code>StrictAudit</code> condition keeps an advisory published overnight from blocking unrelated pull requests, and the lock file makes CI restores reproducible with <code>dotnet restore --locked-mode</code>.
<Project>
<PropertyGroup>
<NuGetAudit>true</NuGetAudit>
<NuGetAuditMode>all</NuGetAuditMode>
<NuGetAuditLevel>moderate</NuGetAuditLevel>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
<!-- High and critical advisories fail only the main-branch and scheduled audit builds,
which pass -p:StrictAudit=true; pull requests and laptops see them as warnings. -->
<PropertyGroup Condition="'$(StrictAudit)' == 'true'">
<WarningsAsErrors>$(WarningsAsErrors);NU1903;NU1904</WarningsAsErrors>
</PropertyGroup>
</Project>
Discussion