Skip to content

/flags/nuget/dotnet-nuget-push-skip-duplicate

dotnet nuget push --skip-duplicate: ignore packages that already exist

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash)updated
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:

bash
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.

OptionEffect
-s, --source NAME_OR_URLTarget feed (a name from nuget.config, a URL or a folder)
-k, --api-key KEYAPI key for the feed; prefer an environment variable or nuget.config
--skip-duplicateTreat an existing ID and version as success
--no-symbolsDo not push the matching .snupkg symbol package
-t, --timeout SECONDSUpload timeout (default 300)
--interactiveAllow a credential provider to prompt (Azure Artifacts)

In a CI pipeline

bash
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-duplicate

The 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.

Sources