stride3d/stride · error · InvalidOperationException
The current thread was expected to be the dispatcher thread.
Error message
The current thread was expected to be the dispatcher thread.
What it means
DispatcherService.EnsureAccess verifies thread affinity for the dispatcher it wraps. With the default inDispatcherThread=true, it throws InvalidOperationException when called from any thread other than the dispatcher thread, protecting WPF objects that can only be touched by their owning thread.
Solutions
- Wrap the affected work in dispatcher.Invoke or dispatcher.InvokeAsync so it runs on the dispatcher thread
- If the check intent was the opposite, pass EnsureAccess(false) to require a non-dispatcher thread
- Remove or re-scope the EnsureAccess call if the code intentionally runs off-thread and marshals later
Example fix
// before
Task.Run(() => { dispatcherService.EnsureAccess(); UpdateUi(); });
// after
dispatcherService.InvokeOrBeginInvoke(() => UpdateUi()); // work marshaled onto dispatcher thread Defensive patterns
Strategy: try-catch
Validate before calling
if (dispatcherService.CheckAccess()) DoWork(); else dispatcherService.Invoke(() => DoWork());
Type guard
bool OnDispatcher(DispatcherService s) => s.CheckAccess();
Try / catch
try { s.EnsureAccess(); } catch (InvalidOperationException ex) when (ex.Message.Contains("expected to be the dispatcher thread")) { dispatcher.Invoke(Work); } Prevention
- Marshal UI work with Invoke/InvokeAsync
- Never touch UI-bound state from Task.Run
- Use EnsureAccess as assertion during development
When it happens
Trigger: Calling EnsureAccess() (or EnsureAccess(true)) from a background/worker thread; performing UI updates inside Task.Run or a background thread callback and asserting dispatcher affinity.
Common situations: Updating observable collections or view models from async work or ThreadPool threads; forgetting to marshal work with dispatcher.Invoke/InvokeAsync; unit tests running assertions on non-UI threads.
Related errors
- A dispatcher lock must be created from a different thread…
- The current thread was expected to be different from the…
- This method must be invoked from the dispatcher thread
- Trying to lock while another thread owns the lock.
- Trying to unlock while another thread owns the lock.
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/288a1304cdaa09b8.
Report an issue: GitHub.
Appendix: source
Thrown at sources/presentation/Stride.Core.Presentation.Wpf/View/DispatcherService.cs:118
[NotNull]
public static Task<TResult> InvokeTask<TResult>([NotNull] Dispatcher dispatcher, Func<Task<TResult>> task, CancellationToken token = default)
{
var tcs = new TaskCompletionSource<TResult>();
dispatcher.InvokeAsync(async () => tcs.SetResult(await task()), DispatcherPriority.Normal, token);
return tcs.Task;
}
/// <inheritdoc/>
public bool CheckAccess()
{
return Thread.CurrentThread == dispatcher.Thread;
}
/// <inheritdoc/>
public void EnsureAccess(bool inDispatcherThread = true)
{
if (inDispatcherThread && Thread.CurrentThread != dispatcher.Thread)
throw new InvalidOperationException("The current thread was expected to be the dispatcher thread.");
if (!inDispatcherThread && Thread.CurrentThread == dispatcher.Thread)
throw new InvalidOperationException("The current thread was expected to be different from the dispatcher thread.");
}
}
}
View on GitHub (pinned to 96fad776d2)