Skip to content

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

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

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
Copy link
Copy Markdown
Contributor

@droyad droyad commented Jun 4, 2026

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

1 participant