dotnet/maui · error · InvalidCastException

renderer must be a Page renderer.

Error message

renderer must be a Page renderer.

What it means

In TabbedRenderer.SetTabBarItem, the renderer's Element is cast to Page (line 471: 'var page = renderer.Element as Page;'). Only Page-derived elements belong in a TabbedPage's children. If the cast fails (Element is not a Page), an InvalidCastException is thrown with a clear message, because the tab bar item (title/icon/automation id) requires a Page.

Source

Thrown at src/Compatibility/Core/src/iOS/Renderers/TabbedRenderer.cs:469

		}

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

		void IEffectControlProvider.RegisterEffect(Effect effect)
		{
			VisualElementRenderer<VisualElement>.RegisterEffect(effect, View);
		}

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

			var icons = await GetIcon(page);
			renderer.ViewController.TabBarItem = new UITabBarItem(page.Title, icons?.Item1, icons?.Item2)
			{
				Tag = Tabbed.Children.IndexOf(page),
				AccessibilityIdentifier = page.AutomationId
			};
			icons?.Item1?.Dispose();
			icons?.Item2?.Dispose();
		}

		void UpdateSelectedTabColors()
		{
			if (Tabbed == null || TabBar == null || TabBar.Items == null)
				return;

			if (Tabbed.IsSet(TabbedPage.SelectedTabColorProperty) && Tabbed.SelectedTabColor != null)
			{

View on GitHub (pinned to f377ff1c5e)

Solutions

  1. Ensure every child of TabbedPage is a Page subclass (ContentPage, NavigationPage, etc.).
  2. Wrap non-page content in a ContentPage before adding to Children.
  3. Validate ItemsSource element types when data-binding a TabbedPage.

Example fix

// before
tabbedPage.Children.Add(new StackLayout()); // not a Page

// after
var page = new ContentPage { Content = new StackLayout() };
tabbedPage.Children.Add(page);
Defensive patterns

Strategy: type-guard

Validate before calling

foreach (var child in tabbedPage.Children)
    if (!(child is Page)) throw new InvalidOperationException($"{child} is not a Page");

Type guard

static bool IsValidTabChild(object element) => element is Page;

Prevention

When it happens

Trigger: Adding a non-Page VisualElement as a child of a TabbedPage, or a custom renderer reporting a non-Page Element. TabbedPage.Children should only contain Page instances.

Common situations: Mistakenly adding a layout/ContentView directly to TabbedPage.Children instead of wrapping it in a ContentPage; custom renderers that swap the Element to a non-Page type; binding a TabbedPage to a source yielding non-Page items.

Related errors


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