Skip to content

Fix bare NullReferenceException for missing NuGet V3 package version (FD-440)#1989

Closed
droyad wants to merge 1 commit into
mainfrom
robw/fd-440-nuget-v3-missing-version-nre
Closed

Fix bare NullReferenceException for missing NuGet V3 package version (FD-440)#1989
droyad wants to merge 1 commit into
mainfrom
robw/fd-440-nuget-v3-missing-version-nre

Conversation

@droyad

@droyad droyad commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Problem

When Calamari downloads a package from a NuGet V3 feed and the requested version does not exist on the feed, the deployment fails with an opaque NullReferenceException instead of a clear "package/version not found" message:

System.NullReferenceException: Object reference not set to an instance of an object.
   at NuGet.Commands.SourceRepositoryDependencyProvider.GetPackageDownloaderAsync(...)
   at Calamari.Integration.Packages.NuGet.NuGetV3LibDownloader.DownloadPackage(...)
   at Calamari.Integration.Packages.NuGet.InternalNuGetPackageDownloader...
   at Calamari.Integration.Packages.Download.NuGetPackageDownloader.DownloadPackage(...)
   at Calamari.Commands.DownloadPackageCommand.Execute(...)

The NRE is thrown inside the (forked) NuGet client library, not Calamari. SourceRepositoryDependencyProvider.GetPackageDownloaderAsync retrieves a downloader from the inner FindPackageByIdResource and then unconditionally calls SetThrottle/SetExceptionHandler on it — but for a version that isn't on the feed, that downloader is null. Calamari calls the API correctly; it just can't stop the library dereferencing its own null.

A customer who selects a version that was never published (CI didn't push it, wrong version selected, etc.) gets a meaningless NullReferenceException with no hint that the real issue is a missing package version.

Scope

  • Affects the "download package on the deployment target" path (the Calamari NuGetV3LibDownloader). The server-side acquisition path (ExternalHttpNuGetPackageFeed) already surfaces a clean "NotFound" message — different code path.
  • Independent of feed authentication — reproduced anonymously against api.nuget.org. Bad-credentials (401) is a separate, already-handled case.
  • Not a day-one bug: the offending GetPackageDownloaderAsync call replaced the older CopyToAsync path in commit ea707ed8c ("#6965 Updated to latest version of NuGet.Commands", 2021-08-09) and has had no null guard since.

Reproduction

Reproduced end-to-end in the product: https://main.testoctopus.app/app#/Spaces-203/projects/download-problem/deployments/releases/0.0.3/deployments/Deployments-98822

  1. Create a step that uses a package from a NuGet (V3) feed
  2. Mark the package as download on target
  3. Create a release with a package version that doesn't exist
  4. Deploy

The added integration test (GivesActionableErrorWhenV3FeedIsMissingTheRequestedVersion) reproduces the exact NRE against api.nuget.org when the guard is absent, and confirms the actionable message when present.

Fixes FD-440.

🤖 Generated with Claude Code

…(FD-440)

When Calamari downloads a package from a NuGet V3 feed and the requested
version does not exist on the feed, the deployment failed with an opaque
NullReferenceException instead of a clear "version not found" message.

The NRE originates inside the (forked) NuGet.Commands
SourceRepositoryDependencyProvider.GetPackageDownloaderAsync: the inner
FindPackageByIdResource returns a null downloader for an absent version,
which the provider then dereferences unconditionally. Calamari calls the
API correctly and cannot stop the library dereferencing its own null, so
NuGetV3LibDownloader now pre-checks that the version exists and throws an
actionable error that folds into the existing retry messaging.

This affects the "download on target" path (Calamari) and is independent
of feed authentication; reproduced anonymously against api.nuget.org.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@octopus-hideaki octopus-hideaki left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@droyad

droyad commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #1994, which goes straight to FindPackageByIdResource and null-checks it directly — removing the buggy SourceRepositoryDependencyProvider wrapper from the path and avoiding the double metadata fetch this PR's DoesPackageExistAsync pre-check introduces. Closing in favour of #1994.

@droyad droyad closed this Jun 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants