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

  1. Create the BindableObject on the UI/main thread where Dispatcher.GetForCurrentThread() is available.
  2. Ensure Application.Current is initialized before using Controls off the UI thread.
  3. 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

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


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