Skip to content

Handle duplicate static web asset identities gracefully#53305

Open
ilonatommy wants to merge 4 commits intodotnet:mainfrom
ilonatommy:fix/handle-duplicate-static-web-assets
Open

Handle duplicate static web asset identities gracefully#53305
ilonatommy wants to merge 4 commits intodotnet:mainfrom
ilonatommy:fix/handle-duplicate-static-web-assets

Conversation

@ilonatommy
Copy link
Member

Summary

Multiple StaticWebAssets MSBuild tasks crash with ArgumentException: An item with the same key has already been added when candidate assets contain duplicate Identity keys. This PR handles duplicates gracefully by keeping the first occurrence and skipping subsequent ones.

What causes duplicates

Two known scenarios produce duplicate asset identities:

  1. Hosted Blazor WASM projects referencing multiple WASM client projects — dotnet.js.map from microsoft.netcore.app.runtime.mono.browser-wasm appears multiple times via different dependency paths. This blocks the dotnet/aspnetcore CI codeflow PR (aspnetcore#65673).

  2. Blazor Web App referencing another Blazor Web Appblazor.web.js from Microsoft.AspNetCore.App.Internal.Assets appears twice (repro: DanielSundberg/DiscoverPrecompressedAssets).

What this PR fixes

All four code paths that build Identity-keyed dictionaries from static web asset items:

File Change
DiscoverPrecompressedAssets.cs ToDictionaryContainsKey loop with diagnostic log
StaticWebAsset.ToAssetDictionary() .AddContainsKey guard (used by 5 other tasks)
GenerateStaticWebAssetsManifest.cs ToDictionaryContainsKey loop
GenerateStaticWebAssetEndpointsManifest.cs ToDictionaryContainsKey loop

Verified locally

  • Reproduced crash on dotnet/aspnetcore codeflow branch (darc-main-b867beb1-...) with SDK 11.0.100-preview.3.26128.104
  • Verified fixHostedInAspNet.Server builds successfully after patching
  • Reproduced crash on dotnet/sdk#52089 minimal repro (BlazorApp referencing BlazorPlugin)
  • Verified fix — the DiscoverPrecompressedAssets crash is eliminated

What this does NOT fix

For the Blazor-references-Blazor scenario (#52089), eliminating the ArgumentException crash surfaces a different, pre-existing error:

Conflicting assets with the same target path '_framework/blazor.server.js'

This conflict occurs because the same framework asset arrives from two projects with different SourceType (Discovered vs Project). The proper architectural fix for this is tracked in #53135 (Framework SourceType by @javiercn), which prevents framework asset duplicates from being generated in the first place.

This PR and #53135 are complementary:

Fixes #52089
Fixes #52647

Multiple StaticWebAssets MSBuild tasks crash with ArgumentException when
candidate assets contain duplicate Identity keys. This happens when:
- Hosted Blazor WASM projects reference multiple WASM client projects,
  causing dotnet.js.map from microsoft.netcore.app.runtime.mono.browser-wasm
  to appear multiple times via different dependency paths.
- A Blazor Web App project references another Blazor Web App project,
  causing blazor.web.js from Microsoft.AspNetCore.App.Internal.Assets
  to appear twice.

Fix all four code paths that build Identity-keyed dictionaries:
- DiscoverPrecompressedAssets.Execute(): ToDictionary → ContainsKey loop
- StaticWebAsset.ToAssetDictionary(): .Add → ContainsKey guard
- GenerateStaticWebAssetsManifest: ToDictionary → ContainsKey loop
- GenerateStaticWebAssetEndpointsManifest: ToDictionary → ContainsKey loop

In all cases, the first occurrence is kept and subsequent duplicates are
silently skipped. DiscoverPrecompressedAssets additionally logs skipped
duplicates at Low message importance for diagnostics.

Note: For the Blazor-references-Blazor scenario (dotnet#52089), this
fix converts the unhandled ArgumentException crash into a proper
diagnostic error (Conflicting assets with the same target path). The
architectural fix for that scenario is tracked in dotnet#53135
(Framework SourceType), which prevents framework asset duplicates from
being generated in the first place.

Fixes dotnet#52089
Fixes dotnet#52647

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 6, 2026 14:23
@github-actions github-actions bot added the Area-AspNetCore RazorSDK, BlazorWebAssemblySDK, StaticWebAssetsSDK label Mar 6, 2026
@dotnet-policy-service
Copy link
Contributor

Thanks for your PR, @@ilonatommy.
To learn about the PR process and branching schedule of this repo, please take a look at the SDK PR Guide.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR prevents Static Web Assets MSBuild tasks from crashing when static web asset item groups contain duplicate Identity values, by changing Identity-keyed dictionary construction to skip subsequent duplicates.

Changes:

  • Replaced ToDictionary(...) calls with Dictionary + duplicate-guard loops in manifest generation tasks.
  • Updated StaticWebAsset.ToAssetDictionary() to use OSPath.PathComparer and skip duplicate identities instead of throwing.
  • Updated DiscoverPrecompressedAssets to dedupe candidate assets by identity and emit a low-importance diagnostic when duplicates are skipped.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetsManifest.cs Avoids ToDictionary duplicate-key crashes when filtering publish endpoints.
src/StaticWebAssetsSdk/Tasks/GenerateStaticWebAssetEndpointsManifest.cs Avoids ToDictionary duplicate-key crashes when building the endpoints manifest asset map.
src/StaticWebAssetsSdk/Tasks/Data/StaticWebAsset.cs Makes ToAssetDictionary() resilient to duplicate identities (and uses OS-specific path comparer).
src/StaticWebAssetsSdk/Tasks/Compression/DiscoverPrecompressedAssets.cs Dedupes candidates by identity and logs when duplicates are skipped to prevent task failure.

ilonatommy and others added 3 commits March 6, 2026 16:12
- Remove OSPath.PathComparer from ToAssetDictionary since StaticWebAsset.cs
  is shared into BlazorWasmSdk which doesn't include OSPath.cs, causing
  CS0103 build errors. The original code used the default comparer, so this
  restores that behavior while keeping the duplicate-handling fix.

- Add DuplicateAssetIdentityHandlingTest with 6 targeted tests covering:
  - ToAssetDictionary: first occurrence wins, no ArgumentException
  - DiscoverPrecompressedAssets: duplicate candidates handled, compressed
    pairs still discovered
  - GenerateStaticWebAssetsManifest (Publish): no crash with duplicates
  - GenerateStaticWebAssetEndpointsManifest: no crash with duplicates

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Address review feedback: all four duplicate-handling code paths now log at
MessageImportance.Low when a duplicate identity is skipped, including the
SourceId of both the kept and discarded assets for diagnostics.

- ToAssetDictionary: accepts optional TaskLoggingHelper parameter; callers
  can pass Log to enable diagnostics without breaking existing signatures
- GenerateStaticWebAssetsManifest: logs skipped duplicates in publish
  endpoint filtering
- GenerateStaticWebAssetEndpointsManifest: logs skipped duplicates in
  manifest asset collection
- DiscoverPrecompressedAssets: already had logging (unchanged)

Also switched ContainsKey+indexer patterns to TryGetValue to satisfy
CA1854 analyzer rule (avoid double dictionary lookup).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When duplicate asset identities have differing metadata (SourceId,
RelativePath, or TargetPath), emit a build warning instead of a
low-importance message. Identical duplicates (same file via multiple
dependency paths) remain at MessageImportance.Low since they are
harmless and expected in multi-WASM-project scenarios.

This distinction helps surface cases where deduplication silently
loses meaningful data (e.g., same file mapped to different target
paths) while keeping the noise down for the common identical case.

Updated tests to cover both paths:
- Identical duplicates → Low message, no warning
- Mismatched duplicates → Warning with metadata details

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

Area-AspNetCore RazorSDK, BlazorWebAssemblySDK, StaticWebAssetsSDK

Projects

None yet

2 participants