dotnet/aspnetcore · error · InvalidOperationException

To support navigation locks

Error message

To support navigation locks, {GetType().Name} must override {nameof(HandleLocationChangingHandlerException)}

What it means

Thrown by the default virtual HandleLocationChangingHandlerException when a registered location-changing handler throws a non-cancellation exception. The base NavigationManager refuses to silently swallow handler failures, so any subclass that opts into navigation locks (by calling RegisterLocationChangingHandler) must override this method to define its own error policy.

Solutions

  1. If you subclass NavigationManager, override HandleLocationChangingHandlerException (log, rethrow, or swallow per your error policy).
  2. Use the framework-provided managers (RemoteNavigationManager on Server, WebAssemblyNavigationManager on WASM) in app code - they already override this.
  3. In tests, derive from the framework manager or a testable base that overrides the lock methods, rather than the abstract base directly.
  4. Make your location-changing handlers exception-safe so the path that reaches this method is never hit.

Example fix

// before: custom NavigationManager subclass lacks the override
public class TestNavManager : NavigationManager { /* only Initialize */ }

// after: provide an error policy for handler exceptions
public class TestNavManager : NavigationManager
{
    protected override void HandleLocationChangingHandlerException(Exception ex, LocationChangingContext context)
        => throw new InvalidOperationException("Navigation handler failed", ex);
}
Defensive patterns

Strategy: validation

Validate before calling

var t = navManager.GetType();
var overriden = t.GetMethod(nameof(NavigationManager.HandleLocationChangingHandlerException), BindingFlags.Instance | BindingFlags.NonPublic)!.DeclaringType != typeof(NavigationManager);
if (!overriden) throw new InvalidOperationException($"{t.Name} cannot host navigation locks: override HandleLocationChangingHandlerException.");

Try / catch

try { manager.RegisterLocationChangingHandler(handler); }
catch (InvalidOperationException ex) when (ex.Message.Contains("HandleLocationChangingHandlerException"))
{ /* swap in a manager that overrides the method, or drop the lock */ }

Prevention

When it happens

Trigger: You call RegisterLocationChangingHandler (directly or via NavigationLock) on a NavigationManager whose concrete type has not overridden HandleLocationChangingHandlerException, and one of the registered handlers throws an exception that is not an OperationCanceledException. The framework's own Server/WebAssembly managers override it; custom test or mock subclasses typically do not.

Common situations: Writing a unit test or custom host that subclasses NavigationManager directly and exercises RegisterLocationChangingHandler; using a mock NavigationManager in tests without overriding the lock-support surface; a handler in a NavigationLock OnLocationChanging callback throws and the manager is a bare test double.

Related errors


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

Appendix: source

Thrown at src/Components/Components/src/NavigationManager.cs:560

            await handler(context);
        }
        catch (OperationCanceledException)
        {
            // Ignore exceptions caused by cancellations.
        }
        catch (Exception ex)
        {
            HandleLocationChangingHandlerException(ex, context);
        }
    }

    /// <summary>
    /// Handles exceptions thrown in location changing handlers.
    /// </summary>
    /// <param name="ex">The exception to handle.</param>
    /// <param name="context">The context passed to the handler.</param>
    protected virtual void HandleLocationChangingHandlerException(Exception ex, LocationChangingContext context)
        => throw new InvalidOperationException($"To support navigation locks, {GetType().Name} must override {nameof(HandleLocationChangingHandlerException)}");

    /// <summary>
    /// Sets whether navigation is currently locked. If it is, then implementations should not update <see cref="Uri"/> and call
    /// <see cref="NotifyLocationChanged(bool)"/> until they have first confirmed the navigation by calling
    /// <see cref="NotifyLocationChangingAsync(string, string?, bool)"/>.
    /// </summary>
    /// <param name="value">Whether navigation is currently locked.</param>
    protected virtual void SetNavigationLockState(bool value)
        => throw new NotSupportedException($"To support navigation locks, {GetType().Name} must override {nameof(SetNavigationLockState)}");

    /// <summary>
    /// Registers a handler to process incoming navigation events.
    /// </summary>
    /// <param name="locationChangingHandler">The handler to process incoming navigation events.</param>
    /// <returns>An <see cref="IDisposable"/> that can be disposed to unregister the location changing handler.</returns>
    public IDisposable RegisterLocationChangingHandler(Func<LocationChangingContext, ValueTask> locationChangingHandler)
    {
        AssertInitialized();

View on GitHub (pinned to 3600ca084e)