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
- Pass the ViewModel type instead: NavigateAsync<MainPageViewModel>() and register the ViewModel-to-View mapping via ViewModelLocator or ViewRegistration.
- Ensure the mapping exists: either [Register]
- 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
- Adopt a strict *ViewModel naming convention and only ever pass those types to the builder.
- Register every ViewModel-to-View mapping so ViewModel keys resolve.
- Enable analyzer/code-review checks that flag VisualElement-derived generics in navigation calls.
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
- The view type ' ' is not a type of Page.
- Unable to convert the value of Type
- A dialog's content must be an Avalonia.Controls.Control
- Cannot destroy .
- The page type ' ' is not supported.
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)