dotnet/reactive · error · ArgumentNullException

dispatcher

Error message

dispatcher

What it means

SubscribeOn(source, dispatcher) throws ArgumentNullException when the dispatcher parameter is null. The dispatcher is required to construct the CoreDispatcherScheduler used to marshal subscription (and disposal) work onto the UI thread. The library validates both arguments eagerly at call time.

Solutions

  1. Capture the dispatcher on the UI thread at startup (e.g. during OnLaunched) and pass that cached instance instead of resolving it at call time.
  2. Use CoreDispatcherScheduler.Current only on UI threads; on background threads capture the UI dispatcher once and store it in a service.
  3. Validate before calling: if (dispatcher == null) throw or fall back to SubscribeOn(TaskPoolScheduler.Default).
  4. Check that the object whose Dispatcher you pass is not null itself (null dependencyObject.Dispatcher is impossible here since the argument is typed CoreDispatcher, but passing null literals happens in refactors).

Example fix

// before
source.SubscribeOn(Window.Current?.Dispatcher) // null on background thread
// after
private static CoreDispatcher _uiDispatcher;
public static void Init(CoreDispatcher d) => _uiDispatcher = d; // called on UI thread
source.SubscribeOn(_uiDispatcher)
Defensive patterns

Strategy: validation

Validate before calling

if (dispatcher == null)
    throw new InvalidOperationException("Capture the UI dispatcher on the UI thread before calling SubscribeOn");
source.SubscribeOn(dispatcher).Subscribe(handler);

Type guard

bool HasDispatcher(CoreDispatcher d) => d != null;

Try / catch

try { source.SubscribeOn(dispatcher).Subscribe(handler); }
catch (ArgumentNullException ex) when (ex.ParamName == "dispatcher")
{ source.SubscribeOn(DefaultScheduler.Instance).Subscribe(handler); }

Prevention

When it happens

Trigger: Calling observable.SubscribeOn(null), or passing a dispatcher obtained from a null object — e.g. Window.Current is null in background threads/non-UI contexts, or a control's Dispatcher property read after the element was detached in edge cases.

Common situations: Calling SubscribeOn from a background thread where CoreWindow/Window.Current is unavailable; caching a Window reference that is null when the app runs in a hosted/background context; accidentally swapping argument order or passing an uninitialized field.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Platforms/WinRT/Linq/CoreDispatcherObservable.cs:178

        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <param name="source">Source sequence.</param>
        /// <param name="dispatcher">Dispatcher whose associated message loop is used to perform subscription and unsubscription actions on.</param>
        /// <returns>The source sequence whose subscriptions and unsubscriptions happen on the specified dispatcher.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="dispatcher"/> is null.</exception>
        /// <remarks>
        /// Only the side-effects of subscribing to the source sequence and disposing subscriptions to the source sequence are run on the specified dispatcher.
        /// In order to invoke observer callbacks on the specified dispatcher, e.g. to render results in a control, use <see cref="CoreDispatcherObservable.ObserveOn{TSource}(IObservable{TSource}, CoreDispatcher)"/>.
        /// </remarks>
        public static IObservable<TSource> SubscribeOn<TSource>(this IObservable<TSource> source, CoreDispatcher dispatcher)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (dispatcher == null)
            {
                throw new ArgumentNullException(nameof(dispatcher));
            }

            return Synchronization.SubscribeOn(source, new CoreDispatcherScheduler(dispatcher));
        }

        /// <summary>
        /// Wraps the source sequence in order to run its subscription and unsubscription logic on the specified dispatcher.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <param name="source">Source sequence.</param>
        /// <param name="dispatcher">Dispatcher whose associated message loop is used to perform subscription and unsubscription actions on.</param>
        /// <param name="priority">Priority to schedule work items at.</param>
        /// <returns>The source sequence whose subscriptions and unsubscriptions happen on the specified dispatcher.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="dispatcher"/> is null.</exception>
        /// <remarks>
        /// Only the side-effects of subscribing to the source sequence and disposing subscriptions to the source sequence are run on the specified dispatcher.
        /// In order to invoke observer callbacks on the specified dispatcher, e.g. to render results in a control, use <see cref="CoreDispatcherObservable.ObserveOn{TSource}(IObservable{TSource}, CoreDispatcher, CoreDispatcherPriority)"/>.
        /// </remarks>

View on GitHub (pinned to 94b5d5ab91)