stride3d/stride · error · InvalidOperationException

The current thread was expected to be different from the…

Error message

The current thread was expected to be different from the dispatcher thread.

What it means

DispatcherService.EnsureAccess has an inverted mode: when called with inDispatcherThread=false it asserts the current thread is NOT the dispatcher thread. It throws InvalidOperationException if the code is unexpectedly running on the dispatcher thread, used to guard operations designed to run in the background.

Solutions

  1. Move the guarded work onto a background thread (Task.Run, dispatcher.InvokeAsync with background priority, or a worker)
  2. Remove the EnsureAccess(false) assertion if the operation is now legitimate on the dispatcher thread
  3. Restructure the call chain so background helpers are not invoked synchronously from UI handlers

Example fix

// before
DoBackgroundWork(); // called directly on dispatcher thread, EnsureAccess(false) throws
// after
await Task.Run(DoBackgroundWork); // runs off the dispatcher thread
Defensive patterns

Strategy: try-catch

Validate before calling

if (!dispatcherService.CheckAccess()) RunBackground(); else await Task.Run(RunBackground);

Type guard

bool OffDispatcher(DispatcherService s) => !s.CheckAccess();

Try / catch

try { s.EnsureAccess(false); } catch (InvalidOperationException ex) when (ex.Message.Contains("different from the dispatcher thread")) { await Task.Run(Work); }

Prevention

When it happens

Trigger: Calling EnsureAccess(false) from code that ended up executing on the dispatcher thread, e.g. a background helper invoked synchronously from a UI event handler.

Common situations: Long-running work accidentally scheduled on the UI thread causing freezes; refactoring that changed a background invocation into a synchronous call without updating the assertion.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/ca5ba007bfacf3d2. Report an issue: GitHub.

Appendix: source

Thrown at sources/presentation/Stride.Core.Presentation.Wpf/View/DispatcherService.cs:120

        {
            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)