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

This error is thrown by the iOS NavigationPage renderer during its initialization (OnElementChanged/setup). It checks NavPage.CurrentPage for null and throws InvalidOperationException if no root Page exists. A NavigationPage must have at least one Page on its stack before the platform renderer can build its UI — the renderer needs a concrete root view controller to push onto the UINavigationController.

Source

Thrown at src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs:222

		}

		public override void ViewDidLoad()
		{
			base.ViewDidLoad();

			UpdateTranslucent();

			_secondaryToolbar = new SecondaryToolbar { Frame = new RectangleF(0, 0, 320, 44) };
			View.Add(_secondaryToolbar);
			_secondaryToolbar.Hidden = true;

			FindParentFlyoutPage();

			var navPage = NavPage;
			INavigationPageController navPageController = NavPage;
			if (navPage.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.");
			}

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

			UpdateBarBackground();
			UpdateBarTextColor();
			UpdateHideNavigationBarSeparator();
			UpdateUseLargeTitles();

			if (OperatingSystem.IsIOSVersionAtLeast(11))
				SetNeedsUpdateOfHomeIndicatorAutoHidden();

			// If there is already stuff on the stack we need to push it

View on GitHub (pinned to f377ff1c5e)

Solutions

  1. Pass a root Page to the NavigationPage constructor: `new NavigationPage(new MyRootPage())` instead of a parameterless constructor.
  2. If creating NavigationPage dynamically, call `PushAsync(rootPage)` synchronously immediately after construction, before it is rendered/displayed.
  3. If using XAML, nest a child Page inside the NavigationPage element: `<NavigationPage> <ContentPage/> </NavigationPage>`.
  4. Verify the root page is non-null and not removed during the renderer's initialization lifecycle.

Example fix

// before
var navPage = new NavigationPage();
MainPage = navPage;
navPage.PushAsync(new MyRootPage()); // too late — renderer already threw

// after
MainPage = new NavigationPage(new MyRootPage());
Defensive patterns

Strategy: validation

Validate before calling

// Before assigning MainPage, verify the NavigationPage has a root page
if (navPage.CurrentPage == null)
    throw new InvalidOperationException("NavigationPage must have a root Page before use.");
MainPage = navPage;

Type guard

public static bool HasRootPage(NavigationPage navPage)
    => navPage?.CurrentPage != null;

Prevention

When it happens

Trigger: Occurs when a NavigationPage is constructed without a root page (e.g., `new NavigationPage()`) and then rendered on iOS without ever calling PushAsync. Also triggered if the NavigationPage's root page is removed or never set before the iOS renderer's ElementChanged fires. The check at line 222 is `if (navPage.CurrentPage == null)` inside the renderer setup that wires up PushRequested/PopRequested event handlers.

Common situations: 1) Creating `new NavigationPage()` with no argument and then setting it as the MainPage or Application.MainPage before pushing content. 2) Migrating from Xamarin.Forms where the constructor sometimes lazily pushed a page, but the timing differs in MAUI's handler architecture. 3) NavigationPage created in XAML as a bare `<NavigationPage/>` without a child page element. 4) Dynamically replacing NavigationPage content where the old root is removed before the new one is set.

Related errors


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