Skip to content

fix: resolve version-range bom-ref crash when scanning multi-project solutions#1071

Merged
mtsfoni merged 3 commits intomasterfrom
fix/pr903-bom-ref-range
Apr 8, 2026
Merged

fix: resolve version-range bom-ref crash when scanning multi-project solutions#1071
mtsfoni merged 3 commits intomasterfrom
fix/pr903-bom-ref-range

Conversation

@mtsfoni
Copy link
Copy Markdown
Member

@mtsfoni mtsfoni commented Apr 8, 2026

Problem

When scanning a solution with multiple projects, the tool crashes with exit code 7:

Unable to locate valid bom ref for <Package> [1.0.0, 1.0.0]

Root cause

The crash requires this specific topology:

  • ProjectA: directly references Pkg.Shared 2.0.0 + Pkg.Consumer 1.0.0
  • ProjectB: directly references Pkg.Shared 1.0.0
  • Pkg.Consumer's nuspec declares its dep on Pkg.Shared with exact-range notation: [1.0.0]

NuGet stores the exact-range constraint verbatim from the nuspec into project.assets.json. ResolveDependencyVersionRanges in ProjectAssetsFileService.cs tries to resolve it by scanning the current project's runtime packages — but in ProjectA's assets, only 2.0.0 is present, and 2.0.0 does not satisfy [1.0.0]. The range string is left unresolved.

After merging both projects' packages into the BOM, both Shared 1.0.0 (from ProjectB) and 2.0.0 (from ProjectA) are present. The name-only fallback in Runner.cs finds two candidates for the unresolved [1.0.0] range → crash.

Fix

When the name-only fallback finds multiple candidates and dep.Value is a parseable VersionRange, use the range itself to select the single satisfying candidate before reaching the error path. This is a minimal, targeted change to Runner.cs with no changes to the existing resolution logic in ProjectAssetsFileService.cs.

[1.0.0].Satisfies(1.0.0) → true, [1.0.0].Satisfies(2.0.0) → false → exactly one match → correct bom-ref for Shared 1.0.0.

Result

  • Both Shared 1.0.0 and 2.0.0 appear as distinct components in the BOM (correct — scanning a solution is a union of all projects)
  • Pkg.Consumer's dependency edge points to Shared@1.0.0 (what its nuspec declares)
  • Verified against the real-world reproducer from the issue (CDXTest solution with Microsoft.CodeAnalysis.Workspaces.Common at 5.0.0 and 5.3.0)

Testing

Adds an E2E regression test (Issue903Tests) with a two-project solution using fake packages served from a BaGetter Testcontainer. The test:

  1. Verifies the tool succeeds (no crash)
  2. Asserts both versions of the shared package are present as distinct components
  3. Asserts the consumer's dependency edge points to the correct (1.0.0) version, not 2.0.0

Closes #903

@mtsfoni mtsfoni requested a review from a team as a code owner April 8, 2026 20:26
@mtsfoni mtsfoni force-pushed the fix/pr903-bom-ref-range branch from 1070dd0 to d08d56e Compare April 8, 2026 20:33
mtsfoni added 3 commits April 8, 2026 22:40
Adds a failing E2E test that reproduces the 'Unable to locate valid bom ref
for <pkg> [x.y.z, x.y.z]' crash. The scenario is a solution with two projects
where one directly pins TestPkg.Shared 2.0.0 and the other depends on
TestPkg.Consumer 1.0.0 whose nuspec declares TestPkg.Shared [1.0.0, 1.0.0].
That exact-range notation in project.assets.json defeats the bomRefLookup in
Runner.cs and the name-only fallback fails because two versions are present.

Signed-off-by: Michael Tsfoni <80639729+mtsfoni@users.noreply.github.com>
…solutions

When a package's nuspec declares an exact-range dependency (e.g. [1.0.0])
and the project that consumes it has resolved a higher version of the same
package directly, NuGet stores the range string verbatim in project.assets.json.
ResolveDependencyVersionRanges cannot resolve it within that project's assets
(2.0.0 does not satisfy [1.0.0]), leaving the range unresolved.

In a multi-project solution where another project resolves 1.0.0, the merged
BOM contains both versions. The name-only fallback in Runner.cs then finds two
candidates and crashes with 'Unable to locate valid bom ref for X [1.0.0, 1.0.0]'.

Fix: when the name-only fallback finds multiple candidates and dep.Value is a
parseable VersionRange, use the range to select the single satisfying candidate
before falling back to the error path.

Adds a two-project E2E regression test that reproduces the exact topology from
the issue report and asserts both the tool succeeds and the dependency edge
points to the correct version.

Signed-off-by: Michael Tsfoni <80639729+mtsfoni@users.noreply.github.com>
Signed-off-by: Michael Tsfoni <80639729+mtsfoni@users.noreply.github.com>
@mtsfoni mtsfoni force-pushed the fix/pr903-bom-ref-range branch from 0c98ab7 to 29cadca Compare April 8, 2026 20:40
@mtsfoni mtsfoni merged commit 0db2de6 into master Apr 8, 2026
16 of 17 checks passed
@mtsfoni mtsfoni deleted the fix/pr903-bom-ref-range branch April 8, 2026 20:44
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