dotnet/maui · error · InvalidOperationException

NavigationPage must have a root Page before being used. Eith

Error message

NavigationPage must have a root Page before being used. Either call PushAsync with a valid Page, or pass a Page to the constructor before usage.

What it means

NavigationPageRenderer.Init requires NavigationPage.CurrentPage to be non-null before wiring up navigation events. CurrentPage is null when the NavigationPage was constructed without a root page and nothing has been pushed yet (the parameterless NavigationPage() ctor leaves it null). The renderer cannot function without a page to display, so it throws rather than rendering an empty shell.

Source

Thrown at src/Compatibility/Core/src/GTK/Renderers/NavigationPageRenderer.cs:222

			else if (e.PropertyName == NavigationPage.BarTextColorProperty.PropertyName)
				UpdateBarTextColor();
			else if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
				UpdateBackgroundColor();
			else if (e.PropertyName ==
				PlatformConfiguration.GTKSpecific.NavigationPage.BackButtonIconProperty.PropertyName)
				UpdateBackButtonIcon();
			else if (e.PropertyName == NavigationPage.CurrentPageProperty.PropertyName)
				UpdateCurrentPage();
			else if (e.PropertyName == NavigationPage.HasNavigationBarProperty.PropertyName)
				UpdateToolBar();
		}

		private void Init()
		{
			ConfigurePageRenderer();

			if (Page.CurrentPage == null)
				throw new InvalidOperationException(
					"NavigationPage must have a root Page before being used. Either call PushAsync with a valid Page, or pass a Page to the constructor before usage.");

			_toolbarTracker.Navigation = Page;
			_currentPage = Page.CurrentPage;
			UpdateCurrentPage();

			NavigationController.PushRequested += OnPushRequested;
			NavigationController.PopRequested += OnPopRequested;
			NavigationController.PopToRootRequested += OnPopToRootRequested;
			NavigationController.RemovePageRequested += OnRemovedPageRequested;
			NavigationController.InsertPageBeforeRequested += OnInsertPageBeforeRequested;

			UpdateBarBackgroundColor();
			UpdateBarTextColor();

			NavigationController.Pages.ForEach(async p => await PushPageAsync(p, false));

			UpdateBackgroundColor();

View on GitHub (pinned to f377ff1c5e)

Solutions

  1. Pass a root page to the constructor: var nav = new NavigationPage(new ContentPage()); MainPage = nav;
  2. If using the parameterless ctor, call Navigation.PushAsync(rootPage) before the page is rendered (before assigning to MainPage).
  3. Ensure any custom NavigationPage subclass always sets a root before it becomes visible.

Example fix

// before
var nav = new NavigationPage();
MainPage = nav; // throws in renderer: CurrentPage null

// after
var nav = new NavigationPage(new HomePage());
MainPage = nav;
Defensive patterns

Strategy: validation

Validate before calling

var nav = new NavigationPage();
if (nav.CurrentPage == null)
    nav.PushAsync(new ContentPage()); // or construct with a root
MainPage = nav;

Type guard

static bool HasRoot(NavigationPage np) => np.CurrentPage != null;

Prevention

When it happens

Trigger: Creating new NavigationPage() (no root) and then setting it as MainPage or otherwise rendering it before a PushAsync. Concretely: NavigationPage.CurrentPage == null at the time the GTK renderer initializes.

Common situations: Using the parameterless NavigationPage() constructor intending to push pages later, but the page is rendered immediately. Refactoring that moves the root-page push after MainPage assignment. Migrating from a backend that tolerated an empty NavigationPage transiently.

Related errors


AI-assisted analysis of dotnet/maui@f377ff1c5e (2026-08-13). Data as JSON: /api/errors/6fb3a5fda40c0ed7. Report an issue: GitHub.