dotnet/aspnetcore · critical · InvalidOperationException
The current thread is not associated with the Dispatcher…
Error message
The current thread is not associated with the Dispatcher. Use InvokeAsync() to switch execution to the Dispatcher when triggering rendering or component state.
What it means
Blazor UI operations (rendering, StateHasChanged, navigation, component state changes) must execute on the Dispatcher's thread. Calling them from a background thread without marshaling throws InvalidOperationException. This protects single-threaded UI consistency.
Solutions
- Wrap UI-touching code in InvokeAsync(() => { ... }).
- Use the component's Dispatcher explicitly: Dispatcher.InvokeAsync(...).
- Avoid fire-and-forget async patterns; await or use Channel/observable patterns marshaled via InvokeAsync.
Example fix
// before
_ = Task.Run(() => { _data = Load(); StateHasChanged(); });
// after
_ = Task.Run(async () => {
var data = await Load();
await InvokeAsync(() => { _data = data; StateHasChanged(); });
}); Defensive patterns
Strategy: validation
Validate before calling
// Marshal UI work onto the Dispatcher before touching component state.
if (!Dispatcher.CheckAccess())
await Dispatcher.InvokeAsync(() => UpdateState());
else
UpdateState(); Prevention
- Always wrap UI-touching callbacks (timers, events, async) in InvokeAsync.
- Avoid fire-and-forget _ = Task.Run(...) that touches component state.
- Use Dispatcher.CheckAccess() defensively in cross-thread event handlers.
When it happens
Trigger: Triggering StateHasChanged, NavigationManager.NavigateTo, or component state mutations from Task.Run, a System.Threading.Timer callback, an async event, or any non-Dispatcher thread.
Common situations: Fire-and-forget async work, background timers, external event subscriptions (e.g. a hub or message bus), or long-running tasks that touch UI state on completion.
Related errors
- Could not find any element matching selector
- Either assemblyName or dotNetObjectId must have a non null…
- For instance method calls, assemblyName should be null…
- No call dispatcher has been set.
- The ' ' selector is not supported.
AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11).
Data as JSON: /api/errors/a1758347a51b5ba0.
Report an issue: GitHub.
Appendix: source
Thrown at src/Components/Components/src/Dispatcher.cs:32
/// <summary>
/// Creates a default instance of <see cref="Dispatcher"/>.
/// </summary>
/// <returns>A <see cref="Dispatcher"/> instance.</returns>
public static Dispatcher CreateDefault() => new RendererSynchronizationContextDispatcher();
/// <summary>
/// Provides notifications of unhandled exceptions that occur within the dispatcher.
/// </summary>
internal event UnhandledExceptionEventHandler? UnhandledException;
/// <summary>
/// Validates that the currently executing code is running inside the dispatcher.
/// </summary>
public void AssertAccess()
{
if (!CheckAccess())
{
throw new InvalidOperationException(
"The current thread is not associated with the Dispatcher. " +
"Use InvokeAsync() to switch execution to the Dispatcher when " +
"triggering rendering or component state.");
}
}
/// <summary>
/// Returns a value that determines whether using the dispatcher to invoke a work item is required
/// from the current context.
/// </summary>
/// <returns><c>true</c> if invoking is required, otherwise <c>false</c>.</returns>
public abstract bool CheckAccess();
/// <summary>
/// Invokes the given <see cref="Action"/> in the context of the associated <see cref="Renderer"/>.
/// </summary>
/// <param name="workItem">The action to execute.</param>
/// <returns>A <see cref="Task"/> that will be completed when the action has finished executing.</returns>View on GitHub (pinned to 3600ca084e)