dotnet/aspnetcore · error · InvalidOperationException

The static asset ' ' is already mapped to .

Error message

The static asset '{property.Value}' is already mapped to {value.Url}.

What it means

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.

Solutions

  1. Inspect the resource list passed to ResourceAssetCollection and find the two entries whose 'label' property collides (case-insensitive).
  2. Give each distinct asset a unique label, or de-duplicate the source manifest so only one entry per logical asset remains.
  3. If the duplication comes from multiple assemblies, scope or filter the resource providers contributing to the collection.
  4. Add a unit test that asserts labels are unique over the produced resource set.

Example fix

// before — two assets share the label 'app.css'
resources.Add(new ResourceAsset("app.min.css", new(){ new("label","app.css") }));
resources.Add(new ResourceAsset("app.debug.css", new(){ new("label","app.css") }));
// throws: The static asset 'app.css' is already mapped to app.min.css.

// after — unique labels
resources.Add(new ResourceAsset("app.min.css", new(){ new("label","app.css") }));
resources.Add(new ResourceAsset("app.debug.css", new(){ new("label","app.debug.css") }));
Defensive patterns

Strategy: validation

Validate before calling

// De-duplicate resource labels before constructing the ResourceAssetCollection.
var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
foreach (var r in resources)
    foreach (var p in r.Properties ?? [])
        if (p.Name.Equals("label", StringComparison.OrdinalIgnoreCase))
            if (!seen.Add(p.Value))
                throw new InvalidOperationException($"Duplicate resource label '{p.Value}'");

Type guard

static bool HasUniqueLabels(IEnumerable<ResourceAsset> resources)
{
    var labels = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
    return resources.SelectMany(r => r.Properties ?? [])
        .Where(p => p.Name.Equals("label", StringComparison.OrdinalIgnoreCase))
        .All(p => labels.Add(p.Value));
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/0ff7a22da64c0ccc. Report an issue: GitHub.

Appendix: source

Thrown at src/Components/Components/src/ResourceAssetCollection.cs:40

    /// <summary>
    /// Initializes a new instance of <see cref="ResourceAssetCollection"/>
    /// </summary>
    /// <param name="resources">The list of resources available.</param>
    public ResourceAssetCollection(IReadOnlyList<ResourceAsset> resources)
    {
        var mappings = new Dictionary<string, ResourceAsset>(StringComparer.OrdinalIgnoreCase);
        var contentSpecificUrls = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        _resources = resources;
        foreach (var resource in resources)
        {
            foreach (var property in resource.Properties ?? [])
            {
                if (property.Name.Equals("label", StringComparison.OrdinalIgnoreCase))
                {
                    if (mappings.TryGetValue(property.Value, out var value))
                    {
                        throw new InvalidOperationException($"The static asset '{property.Value}' is already mapped to {value.Url}.");
                    }
                    mappings[property.Value] = resource;
                    contentSpecificUrls.Add(resource.Url);
                }
            }
        }

        _uniqueUrlMappings = mappings.ToFrozenDictionary();
        _contentSpecificUrls = contentSpecificUrls.ToFrozenSet();
    }

    /// <summary>
    /// Gets the unique content-based URL for the specified static asset.
    /// </summary>
    /// <param name="key">The asset name.</param>
    /// <returns>The unique URL if available, the same <paramref name="key"/> if not available.</returns>
    public string this[string key] => _uniqueUrlMappings.TryGetValue(key, out var value) ? value.Url : key;

View on GitHub (pinned to 3600ca084e)