dotnet/reactive · error · ArgumentNullException

onCompleted

Error message

onCompleted

What it means

The Subscribe(source, onNext, onCompleted) overload throws ArgumentNullException with ParamName "onCompleted" when the onCompleted Action is null. Rx validates it eagerly since AnonymousObserver needs a concrete completion handler; internally the error slot is filled with Stubs.Throw.

Solutions

  1. Pass a non-null onCompleted delegate, e.g. () => { } if completion is irrelevant.
  2. Use the onNext+onError overload if completion handling is truly unnecessary (it stubs onCompleted for you).
  3. Ensure the completion callback is initialized before subscribing.

Example fix

// before
source.Subscribe(x => Handle(x), null); // ArgumentNullException: onCompleted

// after
source.Subscribe(x => Handle(x), () => Console.WriteLine("done"));
Defensive patterns

Strategy: validation

Validate before calling

if (onCompleted is null) onCompleted = () => { }; // or use Subscribe(onNext, onError) overload

Type guard

static bool HasHandlers<T>(Action<T> onNext, Action onCompleted) => onNext is not null && onCompleted is not null;

Try / catch

try { disposable = source.Subscribe(onNext, onCompleted); }
catch (ArgumentNullException ex) when (ex.ParamName == "onCompleted") { /* supply no-op completion handler */ }

Prevention

When it happens

Trigger: Calling source.Subscribe(onNext, null) intending to skip completion handling, or passing an Action variable that was never assigned.

Common situations: Assuming optional trailing arguments may be null (they cannot, despite C# optional-parameter habits); refactoring that removed the completion lambda but kept the call; dynamic handler registration failing to produce the action.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Observable.Extensions.cs:121

        /// <param name="onNext">Action to invoke for each element in the observable sequence.</param>
        /// <param name="onCompleted">Action to invoke upon graceful termination of the observable sequence.</param>
        /// <returns><see cref="IDisposable"/> object used to unsubscribe from the observable sequence.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="onNext"/> or <paramref name="onCompleted"/> is <c>null</c>.</exception>
        public static IDisposable Subscribe<T>(this IObservable<T> source, Action<T> onNext, Action onCompleted)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

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

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

            //
            // [OK] Use of unsafe Subscribe: non-pretentious constructor for an observer; this overload is not to be used internally.
            //
            return source.Subscribe/*Unsafe*/(new AnonymousObserver<T>(onNext, Stubs.Throw, onCompleted));
        }

        /// <summary>
        /// Subscribes an element handler, an exception handler, and a completion handler to an observable sequence.
        /// </summary>
        /// <typeparam name="T">The type of the elements in the source sequence.</typeparam>
        /// <param name="source">Observable sequence to subscribe to.</param>
        /// <param name="onNext">Action to invoke for each element in the observable sequence.</param>
        /// <param name="onError">Action to invoke upon exceptional termination of the observable sequence.</param>
        /// <param name="onCompleted">Action to invoke upon graceful termination of the observable sequence.</param>
        /// <returns><see cref="IDisposable"/> object used to unsubscribe from the observable sequence.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="onNext"/> or <paramref name="onError"/> or <paramref name="onCompleted"/> is <c>null</c>.</exception>

View on GitHub (pinned to 94b5d5ab91)