dotnet/aspnetcore · error · InvalidOperationException

The following routes are ambiguous

Error message

The following routes are ambiguous:
'{existingText}' in '{existing.Handler.FullName}'
'{currentText}' in '{current.Handler.FullName}'

What it means

RouteTableFactory.DetectAmbiguousRoutes runs after all routes are collected and rejects configurations where two distinct components declare route patterns that normalize to the same shape (per InboundRouteEntryAmbiguityEqualityComparer). Ambiguity is fatal because the router would not be able to deterministically pick a handler at request time, so the factory throws InvalidOperationException listing both patterns and both handler types.

Solutions

  1. Read the exception text: it names both patterns and both handler types. Open both components and inspect their [Route] / @page directives.
  2. Change one of the conflicting patterns so the two routes are distinguishable (different path, different parameter shape).
  3. If the duplicate is unintentional (e.g. a leftover page), delete the stale component or remove its [Route].
  4. Run a search for @page "<pattern>" across the solution to find every declaration.

Example fix

// before — two components declare the same route
// Users.cshtml: @page "/users/{Id}"
// Members.cshtml: @page "/users/{Id}"
// after
// Members.cshtml: @page "/members/{Id}"
Defensive patterns

Strategy: validation

Validate before calling

// Detect ambiguous routes at startup before the framework does, with better context.
var byPattern = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
foreach (var (pattern, handler) in discoveredRoutes) {
    if (byPattern.TryGetValue(pattern, out var existing))
        throw new InvalidOperationException($"Ambiguous route '{pattern}': {existing} vs {handler}");
    byPattern[pattern] = handler;
}

Prevention

When it happens

Trigger: Two different page components (in the AppAssembly or any AdditionalAssemblies) whose [Route] attributes resolve to the same parameterized pattern, e.g. [Route("users/{id}")] on two components, or [Route("a/b")] and [Route("a/b/")]. The equality comparer treats these as equal.

Common situations: Copy-pasting a page component and forgetting to change its @page directive; merging two feature branches that both add a route to the same path; multi-assembly apps where a shared library and the host both declare the same route; using route prefixes that collapse the pattern after normalization.

Related errors


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

Appendix: source

Thrown at src/Components/Components/src/Routing/RouteTableFactory.cs:214

            RoutePattern = parsedTemplate,
            UnusedRouteParameterNames = GetUnusedParameterNames(result.AllRouteParameterNames, routeParameterNames!),
        };
    }
    private static void DetectAmbiguousRoutes(TreeRouteBuilder builder)
    {
        var seen = new HashSet<InboundRouteEntry>(new InboundRouteEntryAmbiguityEqualityComparer());
        seen.EnsureCapacity(builder.InboundEntries.Count);

        for (var i = 0; i < builder.InboundEntries.Count; i++)
        {
            var current = builder.InboundEntries[i];

            if (!seen.Add(current))
            {
                seen.TryGetValue(current, out var existing);
                var existingText = existing!.RoutePattern.RawText!.Trim('/');
                var currentText = current.RoutePattern.RawText!.Trim('/');
                throw new InvalidOperationException($"""
                    The following routes are ambiguous:
                    '{existingText}' in '{existing.Handler.FullName}'
                    '{currentText}' in '{current.Handler.FullName}'

                    """);
            }
        }
    }

    private static HashSet<string> GetParameterNames(RoutePattern routeTemplate)
    {
        var parameterNames = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
        foreach (var parameter in routeTemplate.Parameters)
        {
            parameterNames.Add(parameter.Name!);
        }

        return parameterNames;

View on GitHub (pinned to 3600ca084e)