dotnet/reactive · error · ArgumentNullException

nameof(onCompleted)

Error message

nameof(onCompleted)

What it means

The full Do overload forwards onNext, onError, and onCompleted notifications to a witness observer, and the onCompleted callback must be non-null so completion signals can be witnessed. The library throws ArgumentNullException at argument-validation time when onCompleted is null. Nothing is subscribed yet when this fires.

Solutions

  1. Pass a non-null onCompleted handler; use () => { } if completion should be ignored.
  2. If no completion handling is needed, use the single-action overloads like Do(observer, onNext) instead.
  3. Guard the callback variable for null before the call and substitute a no-op.

Example fix

// before
var observer = Do(src, OnNext, OnError, null);
// after
var observer = Do(src, OnNext, OnError, () => { });
Defensive patterns

Strategy: validation

Validate before calling

if (onCompleted == null) onCompleted = () => { };
var observed = Do(observer, onNext, onError, onCompleted);

Type guard

bool IsValidHandler(Delegate d) => d is not null;

Try / catch

try
{
    var observed = Do(observer, onNext, onError, onCompleted);
}
catch (ArgumentNullException ex) when (ex.ParamName == "onCompleted")
{
    var observed = Do(observer, onNext, onError, () => { });
}

Prevention

When it happens

Trigger: Calling Do<TSource>(observer, onNext, onError, onCompleted) (Do.cs:244) with null for the onCompleted argument.

Common situations: Developers assume completion handling is optional and omit it; code that synthesizes handlers from configuration where the completion hook is absent; copy-pasting a two-argument Do call and leaving the last argument empty.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Do.cs:244

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

            return Do(observer, Create<TSource>(_ => default, _ => default, onCompleted));
        }

        public static IAsyncObserver<TSource> Do<TSource>(IAsyncObserver<TSource> observer, Func<TSource, ValueTask> onNext, Func<Exception, ValueTask> onError, Func<ValueTask> onCompleted)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (onNext == null)
                throw new ArgumentNullException(nameof(onNext));
            if (onError == null)
                throw new ArgumentNullException(nameof(onError));
            if (onCompleted == null)
                throw new ArgumentNullException(nameof(onCompleted));

            return Do(observer, Create(onNext, onError, onCompleted));
        }

        public static IAsyncObserver<TSource> Do<TSource>(IAsyncObserver<TSource> observer, IObserver<TSource> witness)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (witness == null)
                throw new ArgumentNullException(nameof(witness));

            return Create<TSource>(
                async x =>
                {
                    try
                    {
                        witness.OnNext(x);
                    }

View on GitHub (pinned to 94b5d5ab91)