dotnet/reactive · error · ArgumentNullException

observer

Error message

observer

What it means

ObservableBase<T>.Subscribe is the base subscription entry point that wraps the observer in an AutoDetachObserver. It throws ArgumentNullException for 'observer' when a null IObserver<T> is passed. The check occurs before scheduling so the error points at the caller's subscription.

Solutions

  1. Pass a fully constructed observer instance to Subscribe
  2. Use delegate-based Subscribe(onNext/onError/onCompleted) overloads which build the observer for you
  3. Null-check the observer variable before subscribing

Example fix

// before
IObserver<int> observer = _observers.TryGet(id); // may be null
observable.Subscribe(observer);
// after
var observer = _observers.TryGet(id) ?? new AnonymousObserver<int>(x => {}, e => {}, () => {});
observable.Subscribe(observer);
Defensive patterns

Strategy: validation

Validate before calling

if (observer == null) throw new InvalidOperationException("observer is not initialized");

Try / catch

try { observable.Subscribe(observer); } catch (ArgumentNullException ex) when (ex.ParamName == "observer") { /* create a valid observer and resubscribe */ }

Prevention

When it happens

Trigger: Calling observable.Subscribe(null) where the concrete observable derives from ObservableBase<T> (ObservableBase.cs:26) — the observer argument is null.

Common situations: Passing a custom IObserver<T> implementation stored in a field that was never assigned; an observer created by a factory that returned null; incorrect overload selection leading to a null first argument.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/ObservableBase.cs:30

    /// <remarks>
    /// If you don't need a named type to create an observable sequence (i.e. you rather need
    /// an instance rather than a reusable type), use the Observable.Create method to create
    /// an observable sequence with specified subscription behavior.
    /// </remarks>
    /// <typeparam name="T">The type of the elements in the sequence.</typeparam>
    public abstract class ObservableBase<T> : IObservable<T>
    {
        /// <summary>
        /// Subscribes the given observer to the observable sequence.
        /// </summary>
        /// <param name="observer">Observer that will receive notifications from the observable sequence.</param>
        /// <returns>Disposable object representing an observer's subscription to the observable sequence.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="observer"/> is <c>null</c>.</exception>
        public IDisposable Subscribe(IObserver<T> observer)
        {
            if (observer == null)
            {
                throw new ArgumentNullException(nameof(observer));
            }

            var autoDetachObserver = new AutoDetachObserver<T>(observer);

            if (CurrentThreadScheduler.IsScheduleRequired)
            {
                //
                // Notice we don't protect this piece of code using an exception handler to
                // redirect errors to the OnError channel. This call to Schedule will run the
                // trampoline, so we'd be catching all exceptions, including those from user
                // callbacks that happen to run there. For example, consider:
                //
                //    Observable.Return(42, Scheduler.CurrentThread)
                //              .Subscribe(x => { throw new Exception(); });
                //
                // Here, the OnNext(42) call would be scheduled on the trampoline, so when we
                // return from the scheduled Subscribe call, the CurrentThreadScheduler moves
                // on to invoking this work item. Too much of protection here would cause the

View on GitHub (pinned to 94b5d5ab91)