dotnet/reactive · error · InvalidOperationException

The current thread has no Dispatcher associated with it.

Error message

The current thread has no Dispatcher associated with it.

What it means

DispatcherScheduler.Current lazily creates a scheduler bound to the Dispatcher of the calling thread. WPF Dispatchers are thread-affine; if the current thread has none (e.g. a thread-pool, task, or console thread), Dispatcher.FromThread returns null and the property throws InvalidOperationException with the NO_DISPATCHER_CURRENT_THREAD message.

Solutions

  1. Capture DispatcherScheduler.Current (or Dispatcher.CurrentDispatcher) on the UI thread first, then pass that instance to ObserveOn/SubscribeOn for use on background threads.
  2. Construct the scheduler explicitly with a known dispatcher: new DispatcherScheduler(Application.Current.Dispatcher).
  3. Fall back gracefully when no dispatcher exists: use ObserveOn(Scheduler.Default) or post via SynchronizationContext.Current instead.

Example fix

// before (runs on pool thread → throws)
source.ObserveOn(DispatcherScheduler.Current).Subscribe(x => ...);
// after: capture on UI thread
var uiScheduler = new DispatcherScheduler(Application.Current.Dispatcher);
source.ObserveOn(uiScheduler).Subscribe(x => ...);
Defensive patterns

Strategy: fallback

Validate before calling

var dispatcher = System.Windows.Threading.Dispatcher.FromThread(Thread.CurrentThread);
var scheduler = dispatcher != null ? new DispatcherScheduler(dispatcher) : Scheduler.Default;

Type guard

static bool HasCurrentDispatcher() => System.Windows.Threading.Dispatcher.FromThread(Thread.CurrentThread) != null;

Try / catch

try { sched = DispatcherScheduler.Current; } catch (InvalidOperationException) { sched = Scheduler.Default; // no dispatcher on this thread }

Prevention

When it happens

Trigger: Accessing DispatcherScheduler.Current from a background thread (Task.Run, ThreadPool, new Thread, Rx SubscribeOnDefaults threads) or before/without a WPF Dispatcher running — e.g. in a console app or unit test with no Dispatcher installed.

Common situations: Calling DispatcherScheduler.Current inside an Rx operator's lambda running on a scheduler pool thread; using it in non-WPF apps that referenced the WindowsThreading package by mistake; tests without a DispatcherFrame/Dispatcher pump.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15). Data as JSON: /api/errors/8d0000ef2c8ad353. Report an issue: GitHub.

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Platforms/Desktop/Concurrency/DispatcherScheduler.cs:33

    /// This scheduler type is typically used indirectly through the <see cref="Linq.DispatcherObservable.ObserveOnDispatcher{TSource}(IObservable{TSource})"/> and <see cref="Linq.DispatcherObservable.SubscribeOnDispatcher{TSource}(IObservable{TSource})"/> methods that use the Dispatcher on the calling thread.
    /// </remarks>
    public class DispatcherScheduler : LocalScheduler, ISchedulerPeriodic
    {
        /// <summary>
        /// Gets the scheduler that schedules work on the current <see cref="System.Windows.Threading.Dispatcher"/>.
        /// </summary>
        [Obsolete(Constants_WindowsThreading.OBSOLETE_INSTANCE_PROPERTY)]
        public static DispatcherScheduler Instance => new(System.Windows.Threading.Dispatcher.CurrentDispatcher);

        /// <summary>
        /// Gets the scheduler that schedules work on the <see cref="System.Windows.Threading.Dispatcher"/> for the current thread.
        /// </summary>
        public static DispatcherScheduler Current
        {
            get
            {
                var dispatcher = System.Windows.Threading.Dispatcher.FromThread(Thread.CurrentThread)
                    ?? throw new InvalidOperationException(Strings_WindowsThreading.NO_DISPATCHER_CURRENT_THREAD);
                return new DispatcherScheduler(dispatcher);
            }
        }

        /// <summary>
        /// Constructs a <see cref="DispatcherScheduler"/> that schedules units of work on the given <see cref="System.Windows.Threading.Dispatcher"/>.
        /// </summary>
        /// <param name="dispatcher"><see cref="DispatcherScheduler"/> to schedule work on.</param>
        /// <exception cref="ArgumentNullException"><paramref name="dispatcher"/> is <c>null</c>.</exception>
        public DispatcherScheduler(System.Windows.Threading.Dispatcher dispatcher)
        {
            Dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher));
            Priority = System.Windows.Threading.DispatcherPriority.Normal;

        }

        /// <summary>
        /// Constructs a <see cref="DispatcherScheduler"/> that schedules units of work on the given <see cref="System.Windows.Threading.Dispatcher"/> at the given priority.

View on GitHub (pinned to 94b5d5ab91)