dotnet/aspnetcore · error · InvalidOperationException

The type {NotFoundPage.FullName} does not have a {typeof(Rou

Error message

The type {NotFoundPage.FullName} does not have a {typeof(RouteAttribute).FullName} applied to it.

What it means

Router also requires the NotFoundPage type to carry a [Route] attribute (its route template is captured to drive the 404 navigation). The check fetches custom attributes of type RouteAttribute and throws if none are present. Without a [Route] the router cannot build a route entry for the not-found page, so the configuration is rejected at startup.

Source

Thrown at src/Components/Components/src/Routing/Router.cs:155

        if (NotFoundPage != null)
        {
#pragma warning disable CS0618 // Type or member is obsolete
            if (NotFound != null)
            {
                throw new InvalidOperationException($"Setting {nameof(NotFound)} and {nameof(NotFoundPage)} properties simultaneously is not supported. Use either {nameof(NotFound)} or {nameof(NotFoundPage)}.");
            }
#pragma warning restore CS0618 // Type or member is obsolete
            if (!typeof(IComponent).IsAssignableFrom(NotFoundPage))
            {
                throw new InvalidOperationException($"The type {NotFoundPage.FullName} " +
                    $"does not implement {typeof(IComponent).FullName}.");
            }

            var routeAttributes = NotFoundPage.GetCustomAttributes(typeof(RouteAttribute), inherit: true);
            if (routeAttributes.Length == 0)
            {
                throw new InvalidOperationException($"The type {NotFoundPage.FullName} " +
                    $"does not have a {typeof(RouteAttribute).FullName} applied to it.");
            }

            var routeAttribute = (RouteAttribute)routeAttributes[0];
            if (routeAttribute.Template != null)
            {
                _notFoundPageRoute = routeAttribute.Template;
            }
        }

        if (!_onNavigateCalled)
        {
            _onNavigateCalled = true;
            await RunOnNavigateAsync(NavigationManager.ToBaseRelativePath(_locationAbsolute), isNavigationIntercepted: false);
        }
        else
        {
            Refresh(isNavigationIntercepted: false);

View on GitHub (pinned to 3600ca084e)

Solutions

  1. Add @page "/not-found" (or equivalent [Route("/not-found")]) to the top of the component referenced by NotFoundPage.
  2. Confirm the attribute survives trimming/publishing — attributes on the type are required at runtime.
  3. Pick a different component that already has a [Route].

Example fix

// before — NotFound.razor has no @page
<h1>404</h1>
// after
@page "/not-found"
<h1>404</h1>
Defensive patterns

Strategy: type-guard

Validate before calling

var attrs = notFoundPage?.GetCustomAttributes(typeof(RouteAttribute), inherit: true);
if (attrs is null || attrs.Length == 0)
    throw new InvalidOperationException($"{notFoundPage} needs a [Route] attribute.");

Type guard

static bool HasRouteAttribute(Type? t)
    => t?.GetCustomAttributes(typeof(RouteAttribute), inherit: true).Length > 0;

Prevention

When it happens

Trigger: Setting NotFoundPage to a component type that has no [Route("...")] attribute on it (e.g. a layout component or a non-routed component).

Common situations: Reusing a layout or shared component as the 404 page; creating a NotFound component and forgetting the @page directive; refactoring a component and removing its @page thinking it is unused.

Related errors


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