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
- Capture DispatcherScheduler.Current (or Dispatcher.CurrentDispatcher) on the UI thread first, then pass that instance to ObserveOn/SubscribeOn for use on background threads.
- Construct the scheduler explicitly with a known dispatcher: new DispatcherScheduler(Application.Current.Dispatcher).
- 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
- Capture the UI dispatcher once on the UI thread and pass it to background code
- Never access DispatcherScheduler.Current from Task.Run/thread-pool lambdas
- Prefer ObserveOn(SynchronizationContext.Current) which degrades gracefully
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
- Value cannot be null. (Parameter 'dispatcher')
- source
- dispatcher
- dispatcher
- The current thread has no Dispatcher associated with it.
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)