dotnet/maui · error · InvalidOperationException
Changing the current page is only allowed if it's being call
Error message
Changing the current page is only allowed if it's being called from the same UI thread.Please ensure that the new page is in the same UI thread as the current page.
What it means
SetCurrent catches exceptions during page transitions and checks for HResult 0x8001010E (RPC_E_WRONG_THREAD). When this HResult is detected, it rethrows as InvalidOperationException with a descriptive message. This fires when code attempts to change the current page from a thread other than the UI thread, violating WinUI/UWP threading rules.
Source
Thrown at src/Compatibility/Core/src/Windows/Platform.cs:425
newPage.Layout(ContainerBounds);
AddPage(newPage);
completedCallback?.Invoke();
_currentPage = newPage;
UpdateToolbarTracker();
await UpdateToolbarItems();
}
catch (Exception error)
{
//This exception prevents the Main Page from being changed in a child
//window or a different thread, except on the Main thread.
//HEX 0x8001010E
if (error.HResult == -2147417842)
throw new InvalidOperationException("Changing the current page is only allowed if it's being called from the same UI thread." +
"Please ensure that the new page is in the same UI thread as the current page.");
throw;
}
}
void RemovePage(Page page)
{
if (_container == null || page == null)
return;
_modalBackgroundPage?.GetCurrentPage()?.SendAppearing();
IVisualElementRenderer pageRenderer = GetRenderer(page);
#pragma warning disable RS0030 // Do not use banned APIs; Panel.Children is banned for performance reasons.
if (_container.Children.Contains(pageRenderer.ContainerElement))
_container.Children.Remove(pageRenderer.ContainerElement);
#pragma warning restore RS0030 // Do not use banned APIsView on GitHub (pinned to f377ff1c5e)
Solutions
- Marshal navigation calls to the UI thread: Device.BeginInvokeOnMainThread(async () => await Navigation.PushAsync(page))
- Use Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () => ...) in WinUI code
- Capture SynchronizationContext or use ConfigureAwait(true) to ensure continuations run on UI thread
- Avoid calling any page-changing API from Task.Run or background event handlers
Example fix
// before
Task.Run(() => {
// runs on thread pool — throws RPC_E_WRONG_THREAD
Application.Current.MainPage = new DifferentPage();
});
// after
Device.BeginInvokeOnMainThread(() => {
Application.Current.MainPage = new DifferentPage();
}); Defensive patterns
Strategy: try-catch
Validate before calling
// Before any page change from async/background context, marshal to UI thread
Device.BeginInvokeOnMainThread(() =>
{
Application.Current.MainPage = newPage;
}); Try / catch
try
{
await SetCurrentAsync(newPage);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("same UI thread"))
{
// Retry on UI thread
Device.BeginInvokeOnMainThread(() =>
{
Application.Current.MainPage = newPage;
});
} Prevention
- Always wrap page-changing calls from async callbacks in Device.BeginInvokeOnMainThread
- Avoid calling navigation from Task.Run or background event handlers
- Capture and use SynchronizationContext or Dispatcher for UI-thread marshalling
- Audit all navigation call sites for thread safety in code review
When it happens
Trigger: A background task, async continuation, timer callback, or event handler on a non-UI thread calls navigation methods or directly changes MainPage. The SetCurrent method performs UI operations (Layout, AddPage, UpdateToolbarItems) that require the UI thread.
Common situations: Task.Run callback modifies UI without Dispatcher marshalling; MessagingCenter subscriber runs on background thread and triggers navigation; event from native WinUI component fires on non-UI thread; long-running async operation's continuation loses the SynchronizationContext.
Related errors
- Could not find or create a renderer for {visualElement}
- PushAsync is not supported globally on Windows, please use a
- PopAsync is not supported globally on Windows, please use a
- PopToRootAsync is not supported globally on Windows, please
- RemovePage is not supported globally on Windows, please use
AI-assisted analysis of dotnet/maui@f377ff1c5e (2026-08-13).
Data as JSON: /api/errors/77d10aceee89a82a.
Report an issue: GitHub.