dotnet/maui · error · InvalidOperationException
BindableObject was not instantiated on a thread with a dispa
Error message
BindableObject was not instantiated on a thread with a dispatcher nor does the current application have a dispatcher.
What it means
FindDispatcher throws InvalidOperationException when no dispatcher can be located for a BindableObject: the object's own dispatcher is null, Dispatcher.GetForCurrentThread() returns null, the handler-level dispatcher is null, and Application.Current?.Dispatcher is null (or the object is an Application itself, which is skipped to avoid recursion). The framework needs a dispatcher to marshal work to the UI thread.
Source
Thrown at src/Controls/src/Core/DispatcherExtensions.cs:47
{
if (appMauiContext.Services.GetOptionalApplicationDispatcher() is IDispatcher appDispatcherServiceDispatcher)
return appDispatcherServiceDispatcher;
// If BO is of type Application then check for its Dispatcher
if (appMauiContext.Services.GetService<IDispatcher>() is IDispatcher appHandlerDispatcher)
return appHandlerDispatcher;
}
// try looking on the static app
// We don't include Application because Application.Dispatcher will call
// `FindDispatcher` if it's _dispatcher property isn't initialized so this
// could cause a Stack Overflow Exception
if (bindableObject is not Application &&
Application.Current?.Dispatcher is IDispatcher appDispatcher)
return appDispatcher;
// no dispatchers found at all
throw new InvalidOperationException("BindableObject was not instantiated on a thread with a dispatcher nor does the current application have a dispatcher.");
}
public static void DispatchIfRequired(this IDispatcher? dispatcher, Action action)
{
dispatcher = EnsureDispatcher(dispatcher);
if (dispatcher.IsDispatchRequired)
{
dispatcher.Dispatch(action);
}
else
{
action();
}
}
public static Task DispatchIfRequiredAsync(this IDispatcher? dispatcher, Action action)
{
dispatcher = EnsureDispatcher(dispatcher);View on GitHub (pinned to f377ff1c5e)
Solutions
- Create the BindableObject on the UI/main thread where Dispatcher.GetForCurrentThread() is available.
- Ensure Application.Current is initialized before using Controls off the UI thread.
- In tests, provide a mock/foreground dispatcher or use the dispatcher test helpers.
Example fix
// before
await Task.Run(() => { var btn = new Button(); btn.Dispatcher; }); // no dispatcher on bg thread
// after
await MainThread.InvokeOnMainThreadAsync(() =>
{
var btn = new Button();
_ = btn.Dispatcher; // dispatcher available
}); Defensive patterns
Strategy: validation
Validate before calling
// Confirm a dispatcher exists before using Controls off the UI thread.
static bool HasDispatcher(BindableObject o) =>
o.Dispatcher is not null
|| Dispatcher.GetForCurrentThread() is not null
|| (Application.Current?.Dispatcher is not null); Prevention
- Create BindableObjects on the main/UI thread.
- Ensure Application.Current is set before off-thread Control use.
- In tests, install a dispatcher (e.g. via test harness) before instantiating Controls.
When it happens
Trigger: Constructing a BindableObject on a background thread with no UI dispatcher, in a headless/test host, or before Application.Current is set, then calling an extension that needs dispatching (e.g. DispatchIfRequired).
Common situations: Unit tests instantiating Controls without a dispatcher; creating BindableObjects in Task.Run before the app is initialized; library code used outside a normal app lifecycle.
Related errors
- The dispatcher was not found and the current application doe
- UIThreadRequired
- UIThreadRequired
- Unable to find Application Services
- Changing the current page is only allowed if it's being call
AI-assisted analysis of dotnet/maui@f377ff1c5e (2026-08-13).
Data as JSON: /api/errors/58dae1918729d308.
Report an issue: GitHub.