Skip to content

[StaticWebAssets] Replace opaque endpoint-manifest collision throw with a targeted BLAZOR107 diagnostic#54934

Draft
PureWeen wants to merge 1 commit into
dotnet:mainfrom
PureWeen:sdk-collision-provenance-diagnostic
Draft

[StaticWebAssets] Replace opaque endpoint-manifest collision throw with a targeted BLAZOR107 diagnostic#54934
PureWeen wants to merge 1 commit into
dotnet:mainfrom
PureWeen:sdk-collision-provenance-diagnostic

Conversation

@PureWeen

Copy link
Copy Markdown
Member

Summary

When two static web assets resolve to the same target path, GenerateStaticWebAssetEndpointsManifest (and its sibling GenerateStaticWebAssetsDevelopmentManifest) crashed with an opaque InvalidOperationException: Sequence contains more than one element from a SingleOrDefault() call. The message named neither the target path nor the colliding assets, so there was no signal about which project or package produced the duplicate.

That opacity actively caused a misdiagnosis of #54779: with no provenance in the error, the natural instinct was to "fix" the SDK task that threw, rather than the upstream producer that generated the duplicate asset.

This PR replaces the throw at both call sites with a targeted BLAZOR107 diagnostic that names every colliding asset's provenance, so the next investigator (human or tooling) is pointed at the producer instead of the symptom.

Note

This is diagnostic hardening, complementary to — not a substitute for — the actual fix for #54779. The collision in that issue originates in the Microsoft.AspNetCore.Components.WebView package and is being addressed in dotnet/aspnetcore. This change ensures that any future producer-side target-path collision fails loudly with actionable provenance.

What changed

  • GenerateStaticWebAssetEndpointsManifest.cs — the throwing site. ChooseNearestAssetKind(...).SingleOrDefault() becomes .ToArray(); if more than one asset is yielded, log BLAZOR107 (listing each asset) and continue; otherwise pick the single asset exactly as before.
  • GenerateStaticWebAssetsDevelopmentManifest.cs — the same latent SingleOrDefault() pattern, given the same treatment.
  • StaticWebAsset.ChooseNearestAssetKind — added an invariant comment documenting that yielding more than one asset means malformed producer-side input, and that callers must surface BLAZOR107 rather than silently collapsing the duplicates here.

Behavior is identical except on the error path: the > 1 branch fires in exactly the cases SingleOrDefault() previously threw; the single-asset and no-asset cases are unchanged.

Example diagnostic output

error BLAZOR107: More than one static web asset matched the target path
'_framework/blazor.modules.json' for asset kind 'Publish'. Each target path must
resolve to a single asset for a given asset kind. This usually means a referenced
project or NuGet package produced an asset that collides with one generated by this
project. Fix the project or package that produces one of these assets so that only a
single asset maps to this target path:
  - Identity='.../blazor.modules.json', SourceId='Microsoft.AspNetCore.Components.WebView', SourceType='Package', AssetKind='All', AssetMode='All'
  - Identity='.../jsmodules.build.manifest.json', SourceId='MyApp', SourceType='Discovered', AssetKind='All', AssetMode='All'

Tests

Two regression tests in GenerateStaticWebAssetEndpointsManifestTest:

Verified the regression test fails before this change (pre-fix logs a single uncoded error: "Sequence contains more than one element") and passes after. No regressions in the endpoints or development-manifest test classes.

Related to #54779.

…th a targeted BLAZOR107 diagnostic

When more than one static web asset resolves to the same target path for a given
asset kind, GenerateStaticWebAssetEndpointsManifest (and the sibling
GenerateStaticWebAssetsDevelopmentManifest) called
ChooseNearestAssetKind(...).SingleOrDefault(), which throws a generic 'Sequence
contains more than one element' exception. That message names neither the
colliding assets nor where they came from, so it points investigators at this
task instead of at the project or NuGet package that produced the duplicate
asset (for example dotnet#54779, where the
Microsoft.AspNetCore.Components.WebView package contributes a second 'All'
blazor.modules.json fallback).

Detect the ambiguous case explicitly and Log.LogError a BLAZOR107 diagnostic that
lists each colliding asset's Identity/SourceId/SourceType/AssetKind/AssetMode and
directs the fix to wherever the asset is produced. Selection behavior is otherwise
unchanged: a single match is still chosen and zero matches still skips the group.
Also document the producer-side contract on ChooseNearestAssetKind so the
duplicate is not 'fixed' by silently collapsing it at the consumption site.

Adds regression coverage for the dotnet#54779 three-asset collision and a
no-false-positive test for the common 'All' + 'Publish' case.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.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