dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'onCompleted')

Error message

Value cannot be null. (Parameter 'onCompleted')

What it means

The Do operator eagerly validates all arguments before returning the lazily-evaluated wrapped sequence. Passing null for the onCompleted action is rejected immediately with ArgumentNullException so the failure surfaces at the call site instead of during enumeration. The library requires every notification callback to be non-null, even ones that would otherwise do nothing.

Solutions

  1. Pass a no-op action `() => { }` for onCompleted if you do not need completion notification
  2. Use the Do(source, onNext) single-callback overload instead, which supplies the completion handler internally
  3. Ensure the callback variable is assigned before calling Do; guard with `?? new Action(() => { })`
  4. Check for null before the call: `if (onCompleted == null) onCompleted = () => {};`

Example fix

// before
var result = source.Do(x => Log(x), null);
// after
var result = source.Do(x => Log(x), () => { });
Defensive patterns

Strategy: validation

Validate before calling

if (source is null) throw new ArgumentNullException(nameof(source));
if (onNext is null) throw new ArgumentNullException(nameof(onNext));
if (onCompleted is null) onCompleted = () => { };

Type guard

static bool IsValidDoArgs<TSource>(IEnumerable<TSource>? s, Action<T>? onCompleted) => s is not null && onCompleted is not null;

Try / catch

try { var result = source.Do(x => Log(x), onCompleted!); ... }
catch (ArgumentNullException ex) when (ex.ParamName == "onCompleted") { Log("onCompleted callback missing; using no-op"); }

Prevention

When it happens

Trigger: Calling Do<TSource>(source, onNext, onCompleted) with a null onCompleted action, e.g. `xs.Do(x => Console.WriteLine(x), null)` or passing a variable that was never assigned.

Common situations: Conditional callback assignment where only onError is populated; refactoring from the 2-argument overload to the 3-argument overload and forgetting the completion callback; deserializing callbacks from configuration where an optional handler resolves to null.

Related errors


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

Appendix: source

Thrown at Ix.NET/Source/System.Interactive/System/Linq/Operators/Do.cs:43

            return DoCore(source, onNext, _ => { }, () => { });
        }

        /// <summary>
        /// Lazily invokes an action for each value in the sequence, and executes an action for successful termination.
        /// </summary>
        /// <typeparam name="TSource">Source sequence element type.</typeparam>
        /// <param name="source">Source sequence.</param>
        /// <param name="onNext">Action to invoke for each element.</param>
        /// <param name="onCompleted">Action to invoke on successful termination of the sequence.</param>
        /// <returns>Sequence exhibiting the specified side-effects upon enumeration.</returns>
        public static IEnumerable<TSource> Do<TSource>(this IEnumerable<TSource> source, Action<TSource> 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));

            return DoCore(source, onNext, _ => { }, onCompleted);
        }

        /// <summary>
        /// Lazily invokes an action for each value in the sequence, and executes an action upon exceptional termination.
        /// </summary>
        /// <typeparam name="TSource">Source sequence element type.</typeparam>
        /// <param name="source">Source sequence.</param>
        /// <param name="onNext">Action to invoke for each element.</param>
        /// <param name="onError">Action to invoke on exceptional termination of the sequence.</param>
        /// <returns>Sequence exhibiting the specified side-effects upon enumeration.</returns>
        public static IEnumerable<TSource> Do<TSource>(this IEnumerable<TSource> source, Action<TSource> onNext, Action<Exception> onError)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (onNext == null)
                throw new ArgumentNullException(nameof(onNext));

View on GitHub (pinned to 94b5d5ab91)