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
- Read the exception text: it names both patterns and both handler types. Open both components and inspect their [Route] / @page directives.
- Change one of the conflicting patterns so the two routes are distinguishable (different path, different parameter shape).
- If the duplicate is unintentional (e.g. a leftover page), delete the stale component or remove its [Route].
- 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
- Search for @page directives across the solution whenever adding a new route.
- Enforce a single source of truth for route strings (constants or a route catalog).
- Add an integration test that loads the route table and asserts no ambiguity.
- After merging branches, run the app once locally to surface duplicate-route exceptions at startup.
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
- Unable to find the provided template
- No component found for route
- Setting and properties simultaneously is not supported. Use…
- The component requires a value for the parameter .
- The component requires a value for the parameter .
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)