dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'context')

Error message

Value cannot be null. (Parameter 'context')

What it means

ArgumentNullException thrown by Synchronization.SubscribeOn<TSource>(IObservable<TSource>, SynchronizationContext) when the SynchronizationContext argument is null. The context is what the subscription call is posted through; a null context cannot route the subscription, so it is rejected up front.

Solutions

  1. Capture the context while on the UI thread (e.g. SynchronizationContext.Current inside a UI constructor or OnLoaded) and pass that captured instance.
  2. Fall back to a scheduler-based overload such as source.SubscribeOn(Scheduler.Default) when no context is available.
  3. Guard with a null check: if context == null, use an alternative subscription path or Scheduler.Immediate.
  4. In tests or console apps, install a context (e.g. new SynchronizationContext()) or avoid the context overload entirely.

Example fix

// before
var ctx = SynchronizationContext.Current; // null on background thread
var scheduled = source.SubscribeOn(ctx);
// after
var ctx = SynchronizationContext.Current ?? new SynchronizationContext();
var scheduled = source.SubscribeOn(ctx);
Defensive patterns

Strategy: validation

Validate before calling

var ctx = context ?? SynchronizationContext.Current ?? new SynchronizationContext();
var scheduled = source.SubscribeOn(ctx);

Type guard

bool IsUsableContext(SynchronizationContext context) => context is not null;

Try / catch

try
{
    var scheduled = source.SubscribeOn(ctx);
}
catch (ArgumentNullException ex) when (ex.ParamName == "context")
{
    var scheduled = source.SubscribeOn(Scheduler.Immediate);
}

Prevention

When it happens

Trigger: Calling source.SubscribeOn(context) where the SynchronizationContext is null — most often SynchronizationContext.Current captured on a background thread where it is null, or a stored context field never initialized.

Common situations: Capturing the UI context on a worker thread instead of the UI thread; console apps and unit tests where no SynchronizationContext exists; async continuations that moved off the UI thread before the context was captured.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Concurrency/Synchronization.cs:106

        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <param name="source">Source sequence.</param>
        /// <param name="context">Synchronization context to perform subscription and unsubscription actions on.</param>
        /// <returns>The source sequence whose subscriptions and unsubscriptions happen on the specified synchronization context.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="context"/> is <c>null</c>.</exception>
        /// <remarks>
        /// Only the side-effects of subscribing to the source sequence and disposing subscriptions to the source sequence are run on the specified synchronization context.
        /// In order to invoke observer callbacks on the specified synchronization context, e.g. to post callbacks to a UI thread represented by the synchronization context, use <see cref="Synchronization.ObserveOn{TSource}(IObservable{TSource}, SynchronizationContext)"/>.
        /// </remarks>
        public static IObservable<TSource> SubscribeOn<TSource>(IObservable<TSource> source, SynchronizationContext context)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

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

            return new SubscribeOnCtxObservable<TSource>(source, context);
        }

        private sealed class SubscribeOnCtxObservable<TSource> : ObservableBase<TSource>
        {
            private sealed class Subscription : IDisposable
            {
                private readonly IObservable<TSource> _source;
                private readonly IObserver<TSource> _observer;
                private readonly SynchronizationContext _context;
                private SingleAssignmentDisposableValue _cancel;

                public Subscription(IObservable<TSource> source, SynchronizationContext context, IObserver<TSource> observer)
                {
                    _source = source;
                    _context = context;

View on GitHub (pinned to 94b5d5ab91)