dotnet/aspnetcore · error · InvalidOperationException

Setting {nameof(NotFound)} and {nameof(NotFoundPage)} proper

Error message

Setting {nameof(NotFound)} and {nameof(NotFoundPage)} properties simultaneously is not supported. Use either {nameof(NotFound)} or {nameof(NotFoundPage)}.

What it means

Router exposes two ways to customize the 404 experience: the legacy NotFound render fragment and the newer NotFoundPage type (a routable component used as the 404 target). They are mutually exclusive because both would attempt to claim the not-found path; setting both at parameter-set time throws InvalidOperationException. The legacy member is decorated [Obsolete] and the warning is suppressed only around the check.

Source

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

        if (AppAssembly == null)
        {
            throw new InvalidOperationException($"The {nameof(Router)} component requires a value for the parameter {nameof(AppAssembly)}.");
        }

        // Found content is mandatory, because even though we could use something like <RouteView ...> as a
        // reasonable default, if it's not declared explicitly in the template then people will have no way
        // to discover how to customize this (e.g., to add authorization).
        if (Found == null)
        {
            throw new InvalidOperationException($"The {nameof(Router)} component requires a value for the parameter {nameof(Found)}.");
        }

        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)
            {

View on GitHub (pinned to 3600ca084e)

Solutions

  1. Choose one mechanism: keep NotFound (render fragment) for inline markup, or NotFoundPage (a routable component) for a full-page 404 — not both.
  2. Remove whichever attribute you are not using from the Router markup.
  3. If migrating, delete NotFound from markup at the same commit that introduces NotFoundPage.

Example fix

// before
<Router AppAssembly="@typeof(Program).Assembly"
        NotFound="@NotFoundTemplate"
        NotFoundPage="@typeof(NotFoundPage)">  <!-- both set -->
// after — pick one
<Router AppAssembly="@typeof(Program).Assembly"
        NotFoundPage="@typeof(NotFoundPage)">
Defensive patterns

Strategy: validation

Validate before calling

// Choose one not-found mechanism; this is a markup-level discipline.
// <Router ... NotFoundPage="@typeof(NotFound)">  // do NOT also set NotFound

Type guard

static bool IsExclusiveNotFoundSet(RenderFragment? notFound, Type? notFoundPage)
    => !(notFound is not null && notFoundPage is not null);

Prevention

When it happens

Trigger: Setting both NotFound="@(...)" and NotFoundPage="typeof(SomeComponent)" on the same <Router> instance. Also by setting them via SetParametersAsync in test code.

Common situations: Migrating from NotFound to NotFoundPage and forgetting to remove the old attribute; copy-pasting across templates that use different approaches; IDE auto-completion adding both attributes.

Related errors


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