dotnet/maui · error · InvalidCastException

renderer must be a Page renderer.

Error message

renderer must be a Page renderer.

What it means

Thrown by the iOS TabbedRenderer.SetTabBarItem when the renderer's VirtualView is not a Page. The TabbedRenderer expects each tab's handler to render a Page-derived element (ContentPage, NavigationPage, etc.). If a non-Page visual element was added to the TabbedPage's Children or the handler was misconfigured, the cast `renderer.VirtualView as Page` returns null and InvalidCastException is thrown.

Source

Thrown at src/Controls/src/Core/Compatibility/Handlers/TabbedPage/iOS/TabbedRenderer.cs:558

				}
			}
		}

		void UpdateCurrentPage()
		{
			if (Tabbed is TabbedPage tabbed)
			{
				var count = tabbed.InternalChildren.Count;
				var index = (int)SelectedIndex;
				tabbed.CurrentPage = index >= 0 && index < count ? tabbed.GetPageByIndex(index) : null;
			}
		}

		async void SetTabBarItem(IPlatformViewHandler renderer)
		{
			var page = renderer.VirtualView as Page;
			if (page == null)
				throw new InvalidCastException($"{nameof(renderer)} must be a {nameof(Page)} renderer.");

			var icons = await GetIcon(page);
			var resizedImage = TabbedViewExtensions.AutoResizeTabBarImage(TraitCollection, icons?.Item1);
			var resizedSelectedImage = TabbedViewExtensions.AutoResizeTabBarImage(TraitCollection, icons?.Item2);
			SetTabBarItem(resizedImage, resizedSelectedImage);
			resizedImage?.Dispose();
			resizedSelectedImage?.Dispose();

			void SetTabBarItem(UIImage image, UIImage selectedImage)
			{
				renderer.ViewController.TabBarItem = new UITabBarItem(page.Title, image, selectedImage)
				{
					Tag = Tabbed?.Children.IndexOf(page) ?? -1,
					AccessibilityIdentifier = page.AutomationId
				};
			}

			icons?.Item1?.Dispose();

View on GitHub (pinned to f377ff1c5e)

Solutions

  1. Ensure every child added to TabbedPage.Children is a Page subclass (ContentPage, NavigationPage, etc.).
  2. If using ContentView as tab content, wrap it in a ContentPage first.
  3. Verify handler mappings if using custom element types — only Page-derived elements are valid tab children.
  4. Prefer the modern MAUI TabbedPage alternatives (Shell with tabs, or TabbedPage with proper Page children) over the legacy compatibility path.
  5. Inspect which tab index triggers the error to identify the problematic child.

Example fix

// before
var tabbedPage = new TabbedPage();
tabbedPage.Children.Add(new ContentView { Content = new Label { Text = "Tab1" } }); // not a Page!

// after
var tabbedPage = new TabbedPage();
tabbedPage.Children.Add(new ContentPage { Title = "Tab1", Content = new Label { Text = "Tab1" } });
Defensive patterns

Strategy: type-guard

Validate before calling

// Verify children are Page types before adding to TabbedPage
foreach (var child in tabbedPage.Children)
{
    if (child is not Page)
        throw new InvalidCastException($"TabbedPage child '{child}' must be a Page.");
}

Type guard

public static bool IsValidTabChild(Element element)
    => element is Page;

Prevention

When it happens

Trigger: At line 558: `var page = renderer.VirtualView as Page; if (page == null) throw new InvalidCastException`. SetTabBarItem is called to configure each tab's UITabBarItem (title, icon). Triggered when the handler associated with a tab index produces a VirtualView that is not a Page subclass. This can happen when: (1) a non-Page element (e.g., a Layout, ContentView directly) is added to TabbedPage.Children; (2) the handler mapping is misconfigured for a custom element type; (3) a handler returns the wrong VirtualView type.

Common situations: 1) Adding a ContentView or Layout directly to TabbedPage.Children instead of a ContentPage. 2) Custom handler registration that maps a non-Page type to the TabbedRenderer's tab slots. 3) TabbedPage is a legacy/compatibility API — migration from Xamarin.Forms where Children contained non-Page items that happened to work. 4) Programmatic creation of TabbedPage with incorrect child types.

Related errors


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