{"record":{"id":"0ff7a22da64c0ccc","repo":"dotnet/aspnetcore","slug":"the-static-asset-property-value-is-already-map","errorCode":null,"errorMessage":"The static asset '{property.Value}' is already mapped to {value.Url}.","messagePattern":"The static asset '(.+?)' is already mapped to (.+?)\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/Components/Components/src/ResourceAssetCollection.cs","lineNumber":40,"sourceCode":"\n    /// <summary>\n    /// Initializes a new instance of <see cref=\"ResourceAssetCollection\"/>\n    /// </summary>\n    /// <param name=\"resources\">The list of resources available.</param>\n    public ResourceAssetCollection(IReadOnlyList<ResourceAsset> resources)\n    {\n        var mappings = new Dictionary<string, ResourceAsset>(StringComparer.OrdinalIgnoreCase);\n        var contentSpecificUrls = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n        _resources = resources;\n        foreach (var resource in resources)\n        {\n            foreach (var property in resource.Properties ?? [])\n            {\n                if (property.Name.Equals(\"label\", StringComparison.OrdinalIgnoreCase))\n                {\n                    if (mappings.TryGetValue(property.Value, out var value))\n                    {\n                        throw new InvalidOperationException($\"The static asset '{property.Value}' is already mapped to {value.Url}.\");\n                    }\n                    mappings[property.Value] = resource;\n                    contentSpecificUrls.Add(resource.Url);\n                }\n            }\n        }\n\n        _uniqueUrlMappings = mappings.ToFrozenDictionary();\n        _contentSpecificUrls = contentSpecificUrls.ToFrozenSet();\n    }\n\n    /// <summary>\n    /// Gets the unique content-based URL for the specified static asset.\n    /// </summary>\n    /// <param name=\"key\">The asset name.</param>\n    /// <returns>The unique URL if available, the same <paramref name=\"key\"/> if not available.</returns>\n    public string this[string key] => _uniqueUrlMappings.TryGetValue(key, out var value) ? value.Url : key;\n","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/3600ca084e9c8b5f4174fc5e747f4c52d2100806/src/Components/Components/src/ResourceAssetCollection.cs#L22-L58","documentation":"ResourceAssetCollection builds an index of static assets keyed by their 'label' property (the developer-facing logical name). Two distinct resources sharing the same label collide because the label is the lookup key for resource resolution at runtime, so the constructor throws InvalidOperationException naming both the duplicate label and the previously-mapped URL. This is a startup-time invariant, not a per-request check.","triggerScenarios":"Two ResourceAsset entries passed to ResourceAssetCollection.Create (or via the framework's resource discovery) where both expose a Properties entry with Name == 'label' and identical Value (case-insensitive). Common with fingerprinted assets that retain the same logical name across builds, or with manually-curated resource manifests that list the same JS/CSS twice.","commonSituations":"Bundling/publishing pipeline emits two assets with the same label (e.g. one minified, one not); static web assets from different assembly roots whose paths collapse to the same label; misconfigured resource manifest JSON; testing with hand-built ResourceAsset arrays that accidentally duplicate.","solutions":["Inspect the resource list passed to ResourceAssetCollection and find the two entries whose 'label' property collides (case-insensitive).","Give each distinct asset a unique label, or de-duplicate the source manifest so only one entry per logical asset remains.","If the duplication comes from multiple assemblies, scope or filter the resource providers contributing to the collection.","Add a unit test that asserts labels are unique over the produced resource set."],"exampleFix":"// before — two assets share the label 'app.css'\nresources.Add(new ResourceAsset(\"app.min.css\", new(){ new(\"label\",\"app.css\") }));\nresources.Add(new ResourceAsset(\"app.debug.css\", new(){ new(\"label\",\"app.css\") }));\n// throws: The static asset 'app.css' is already mapped to app.min.css.\n\n// after — unique labels\nresources.Add(new ResourceAsset(\"app.min.css\", new(){ new(\"label\",\"app.css\") }));\nresources.Add(new ResourceAsset(\"app.debug.css\", new(){ new(\"label\",\"app.debug.css\") }));","handlingStrategy":"validation","validationCode":"// De-duplicate resource labels before constructing the ResourceAssetCollection.\nvar seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\nforeach (var r in resources)\n    foreach (var p in r.Properties ?? [])\n        if (p.Name.Equals(\"label\", StringComparison.OrdinalIgnoreCase))\n            if (!seen.Add(p.Value))\n                throw new InvalidOperationException($\"Duplicate resource label '{p.Value}'\");","typeGuard":"static bool HasUniqueLabels(IEnumerable<ResourceAsset> resources)\n{\n    var labels = new HashSet<string>(StringComparer.OrdinalIgnoreCase);\n    return resources.SelectMany(r => r.Properties ?? [])\n        .Where(p => p.Name.Equals(\"label\", StringComparison.OrdinalIgnoreCase))\n        .All(p => labels.Add(p.Value));\n}","tryCatchPattern":null,"preventionTips":["Validate the resource list for unique labels before passing it to ResourceAssetCollection.Create.","Generate resource labels from a deterministic source (file path + hash) to avoid collisions.","Add a unit test over the produced resource set asserting label uniqueness.","Audit bundling output when fingerprinting changes — labels should remain stable per logical asset."],"tags":["blazor","static-assets","resource-asset","startup","invalidoperationexception"],"backgroundTag":null,"analyzedSha":"3600ca084e9c8b5f4174fc5e747f4c52d2100806","analyzedAt":"2026-08-11T16:32:30.678Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}