/flags/nuget/dotnet-nuget-push-skip-duplicate
dotnet nuget push --skip-duplicate: ignore packages that already exist
- tool
- NuGet
- command
- dotnet nuget push
- flag
- --skip-duplicate
- default
- off (an existing version makes the push fail)
On NuGet servers, package versions are immutable: once Demo.Lib 1.0.0 is on nuget.org, Azure Artifacts or GitHub Packages, pushing the same version again is rejected with 409 Conflict and the push fails. dotnet nuget push --skip-duplicate treats that conflict as a skip and continues, which makes a publish step safe to re-run, for example when a CI pipeline retries or pushes a folder in which only some packages are new. Folder feeds behave differently, as the example shows.
Folder feeds do not reject duplicates
A local folder is a valid push target, which makes it easy to test the command without a server:
dotnet new classlib -o Demo.Lib > /dev/null
dotnet pack Demo.Lib -o out -p:PackageVersion=1.0.0 -v q -nologo > /dev/null 2>&1
mkdir repo
echo "--- first push"
dotnet nuget push out/Demo.Lib.1.0.0.nupkg -s "$PWD/repo" 2>&1 | grep -vE "^ *$|Pushing"; echo "exit: ${PIPESTATUS[0]}"
echo "--- same version again"
dotnet nuget push out/Demo.Lib.1.0.0.nupkg -s "$PWD/repo" 2>&1 | grep -vE "^ *$|Pushing"; echo "exit: ${PIPESTATUS[0]}"
echo "--- again with --skip-duplicate"
dotnet nuget push out/Demo.Lib.1.0.0.nupkg -s "$PWD/repo" --skip-duplicate 2>&1 | grep -vE "^ *$|Pushing"; echo "exit: ${PIPESTATUS[0]}"
find repo -name "*.nupkg"Output
--- first push Your package was pushed. exit: 0 --- same version again Your package was pushed. exit: 0 --- again with --skip-duplicate warn : The option to skip duplicates is not currently supported for this type of push. Your package was pushed. exit: 0 repo/Demo.Lib.1.0.0.nupkg
A folder feed accepts the same version twice: the second push reports success again and exits with 0. --skip-duplicate is not supported for this kind of push, and NuGet says so with a warning before pushing anyway. So a local folder is no place to test duplicate handling; the 409 behaviour, and the skip, only exist on HTTP feeds.
Related push options
| Option | Effect |
|---|---|
| -s, --source NAME_OR_URL | Target feed (a name from nuget.config, a URL or a folder) |
| -k, --api-key KEY | API key for the feed; prefer an environment variable or nuget.config |
| --skip-duplicate | Treat an existing ID and version as success |
| --no-symbols | Do not push the matching .snupkg symbol package |
| -t, --timeout SECONDS | Upload timeout (default 300) |
| --interactive | Allow a credential provider to prompt (Azure Artifacts) |
In a CI pipeline
dotnet pack -c Release -o artifacts
dotnet nuget push "artifacts/*.nupkg" --source https://api.nuget.org/v3/index.json \
--api-key "$NUGET_API_KEY" --skip-duplicateThe wildcard pushes every package of a multi-project solution; with --skip-duplicate, packages whose version did not change are skipped instead of failing the job. Creating packages is covered in dotnet pack.
Pitfalls
- nuget.org deletes nothing on push conflicts and does not allow re-using a version even after unlisting it; plan versions with a CI build number or a tool such as MinVer or Nerdbank.GitVersioning.
- Symbol packages (
.snupkg) are pushed together with the main package and are skipped the same way. - Keep API keys out of command history and logs: read them from a secret variable, as above.
FAQ
What does 409 Conflict mean when pushing a NuGet package?
The feed already has a package with that ID and version. NuGet versions are immutable, so a feed refuses to overwrite them. Bump the version, or pass --skip-duplicate when re-running a pipeline that may push the same version again.
Does --skip-duplicate hide other push errors?
No. Only the 'already exists' conflict is turned into a skip. Authentication failures, invalid packages and network errors still fail the command.
How do I push all packages in a folder?
Use a wildcard: dotnet nuget push "artifacts/*.nupkg" --source NAME --skip-duplicate. Quote the pattern so dotnet expands it, not the shell.