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
- Ensure every child of TabbedPage is a Page subclass (ContentPage, NavigationPage, etc.).
- Wrap non-page content in a ContentPage before adding to Children.
- 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
- Only add Page-derived instances to TabbedPage.Children.
- Wrap non-page content in ContentPage.
- Validate bound ItemsSource element types.
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
- renderer must be a Page renderer.
- Element must be a TabbedPage
- Element must be a TabbedPage
- Element must be of type Frame.
- No test results found.
AI-assisted analysis of dotnet/maui@f377ff1c5e (2026-08-13).
Data as JSON: /api/errors/57f7622176b66bc1.
Report an issue: GitHub.