Package source mapping cannot route version ranges of the same package ID to different feeds
0 reputation · 25 Nov 2023, 12:06 UTC
0 reputation · 25 Nov 2023, 12:06 UTC
Package source mapping in nuget.config binds package ID patterns to specific feeds, but the matching logic considers only the package identifier and ignores the version. This means a mapped feed can serve every version of a matched package ID, including pre-release builds, stable releases, and any future version published to that feed.
The goal is to separate internal pre-release builds from public stable releases for the same package ID by sending them to different feeds. Because the packageSourceMapping element has no version dimension, there is no supported way to express that boundary directly. Teams must either enumerate all package ID prefixes explicitly or add a broad * catch-all pattern, which removes the supply-chain guardrail by allowing any configured feed to serve any package ID.
The mapping also applies to the entire dependency graph, including transitive dependencies, so an unmapped package ID will block restore even when it is not referenced directly by the project. Older tooling before NuGet 5.7 ignores the section entirely.
Is there a supported configuration or extension point that adds version constraints to package source mapping? If not, what is the recommended design for splitting pre-release and stable feeds without a catch-all? How does the lack of version routing affect transitive dependency resolution when different feeds host different versions of the same package ID?
There is no supported configuration or extension point in NuGet to add version constraints to package source mapping. The packageSourceMapping logic operates exclusively on the package identifier (ID). Once a package ID pattern is mapped to a specific feed, NuGet will only query that feed for all versions of that package, regardless of whether they are stable, pre-release, or hosted on a different configured source.
Because mapping applies to the entire dependency graph, the lack of version routing creates a strict boundary. If Package.A is mapped to a public feed, but a transitive dependency Package.B (required by a specific version of A) is only available in a private feed and is not explicitly mapped, the restore process will fail. NuGet will not "fall back" to other feeds to find a missing version if a mapping pattern for that ID exists elsewhere or if the ID is unmapped.
To separate pre-release and stable feeds without resorting to a catch-all * pattern—which eliminates the security benefits of source mapping—consider these two architectural approaches:
Company.Project.Core for stable releases and Company.Project.Core.Beta or Company.Project.Core.Dev for pre-releases. This allows you to map the .Beta pattern to your internal feed and the stable pattern to your public feed.To verify your current mapping behavior, you can run a restore with detailed logging to see which feeds are being queried:
dotnet restore --verbosity detailed
Ensure you are using NuGet 5.7 or later (or the corresponding .NET SDK). Versions prior to 5.7 ignore the packageSourceMapping section entirely, which may lead to "silent" successes that are actually bypassing your security constraints.
Diagnostic Detail: Are you using a package manager with upstream capabilities (e.g., Azure Artifacts), or are you relying solely on a nuget.config file with multiple raw HTTP endpoints?
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.