PrismLibrary/Prism · error · NavigationException

You have referenced a View type and are likely breaking the…

Error message

You have referenced a View type and are likely breaking the MVVM pattern. You should never reference a View type from a ViewModel.

What it means

GetNavigationKey<TViewModel> guards the MVVM pattern: if TViewModel is actually assignable from VisualElement (i.e. a Page/View type was passed where a ViewModel is expected), it throws NavigationException with code MvvmPatternBreak. Prism's builder API resolves navigation keys from ViewModel registrations, so passing a View defeats the design.

Solutions

  1. Pass the ViewModel type instead: NavigateAsync<MainPageViewModel>() and register the ViewModel-to-View mapping via ViewModelLocator or ViewRegistration.
  2. Ensure the mapping exists: either [Register]
  3. If a View type is genuinely required, use the string-key based navigation (NavigateAsync("MainPage")) rather than the ViewModel-typed builder.

Example fix

// before
navigationService.CreateBuilder<MainPage>() // View type -> MVVM break
    .NavigateAsync();
// after
navigationService.CreateBuilder<MainPageViewModel>()
    .NavigateAsync();
Defensive patterns

Strategy: type-guard

Validate before calling

if (typeof(T).IsAssignableFrom(typeof(VisualElement)) || typeof(VisualElement).IsAssignableFrom(typeof(T)))
    throw new ArgumentException($"{typeof(T).Name} is a View; pass the ViewModel type instead.");

Type guard

bool IsViewModel<T>() => !typeof(VisualElement).IsAssignableFrom(typeof(T));

Try / catch

try
{
    await navigationService.CreateBuilder<TViewModel>().NavigateAsync();
}
catch (NavigationException ex) when (ex.Code == NavigationException.MvvmPatternBreak)
{
    logger.LogError(ex, "A View type was passed where a ViewModel was expected");
}

Prevention

When it happens

Trigger: Calling .CreateBuilder<TView>(...) style extension like NavigateAsync<MainPage>() where MainPage : ContentPage, i.e. a View type used as the generic argument instead of its ViewModel.

Common situations: Choosing the page class from IntelliSense autocomplete instead of the ViewModel; projects without a 1:1 ViewModel naming convention; generics where typeof(T) ended up being a VisualElement-derived type.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of PrismLibrary/Prism@358118cd64 (2026-09-15). Data as JSON: /api/errors/44a0a570119fbdc4. Report an issue: GitHub.

Appendix: source

Thrown at src/Maui/Prism.Maui/Navigation/Builder/NavigationBuilderExtensions.cs:21

using Prism.Navigation.Builder;

namespace Prism.Navigation;

public static class NavigationBuilderExtensions
{
    /// <summary>
    /// Creates a <see cref="INavigationBuilder"/> using the current instance of the <see cref="INavigationService"/>.
    /// </summary>
    /// <param name="navigationService">The <see cref="INavigationService"/>.</param>
    /// <returns><see cref="INavigationBuilder"/></returns>
    public static INavigationBuilder CreateBuilder(this INavigationService navigationService) =>
           new NavigationBuilder(navigationService);

    internal static string GetNavigationKey<TViewModel>(object builder)
    {
        var vmType = typeof(TViewModel);
        if (vmType.IsAssignableFrom(typeof(VisualElement)))
            throw new NavigationException(NavigationException.MvvmPatternBreak, typeof(TViewModel).Name);

        if (builder is not IRegistryAware registryAware)
            throw new Exception("The builder does not implement IRegistryAware");

        return registryAware.Registry.GetViewModelNavigationKey(vmType);
    }

    public static INavigationBuilder RelativeBack(this INavigationBuilder builder) =>
        builder.AddSegment("..");

    /// <summary>
    /// This will force the generated Navigation URI to return an Absolute URI resetting the current <see cref="Window"/>'s <see cref="Page"/> property.
    /// </summary>
    /// <param name="builder">The <see cref="INavigationBuilder"/>.</param>
    /// <returns>The <see cref="INavigationBuilder"/>.</returns>
    public static INavigationBuilder UseAbsoluteNavigation(this INavigationBuilder builder) =>
        builder.UseAbsoluteNavigation(true);

View on GitHub (pinned to 358118cd64)