dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'source2')

Error message

Value cannot be null. (Parameter 'source2')

What it means

The Observable.Zip three-source overload throws ArgumentNullException when source2 is null. The public method checks source1, source2, source3, and resultSelector in order; this error means the second sequence failed the check. Supply a non-null IObservable as the second argument.

Solutions

  1. Ensure the second observable passed to Zip is non-null.
  2. Substitute Observable.Empty<TSource2>() or Observable.Never<TSource2>() if the source is optional.
  3. Validate each source for null before calling Zip.

Example fix

// before
first.Zip(secondStream, third, selector); // secondStream == null
// after
var s2 = secondStream ?? Observable.Empty<SecondType>();
first.Zip(s2, third, selector);
Defensive patterns

Strategy: validation

Validate before calling

if (source2 == null) source2 = Observable.Empty<TSource2>();

Type guard

bool IsValid<T>(IObservable<T> s) => s is not null;

Try / catch

try { return Observable.Zip(source1, source2, source3, resultSelector); }
catch (ArgumentNullException ex) when (ex.ParamName == "source2") { /* replace with empty and retry */ }

Prevention

When it happens

Trigger: Calling Observable.Zip(source1, source2, source3, resultSelector) with null as the second observable.

Common situations: A secondary stream (e.g. a settings or heartbeat observable) not yet created when zipping; event-stream registration returning null; merging UI and service streams where the service stream is unavailable.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Multiple.Zip.cs:33

        /// <typeparam name="TSource2">The type of the elements in the second source sequence.</typeparam>
        /// <typeparam name="TSource3">The type of the elements in the third source sequence.</typeparam>
        /// <typeparam name="TResult">The type of the elements in the result sequence, returned by the selector function.</typeparam>
        /// <param name="source1">First observable source.</param>
        /// <param name="source2">Second observable source.</param>
        /// <param name="source3">Third observable source.</param>
        /// <param name="resultSelector">Function to invoke for each series of elements at corresponding indexes in the sources.</param>
        /// <returns>An observable sequence containing the result of combining elements of the sources using the specified result selector function.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source1"/> or <paramref name="source2"/> or <paramref name="source3"/> or <paramref name="resultSelector"/> is null.</exception>
        public static IObservable<TResult> Zip<TSource1, TSource2, TSource3, TResult>(this IObservable<TSource1> source1, IObservable<TSource2> source2, IObservable<TSource3> source3, Func<TSource1, TSource2, TSource3, TResult> resultSelector)
        {
            if (source1 == null)
            {
                throw new ArgumentNullException(nameof(source1));
            }

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

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

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

            return s_impl.Zip(source1, source2, source3, resultSelector);
        }

        /// <summary>
        /// Merges the specified observable sequences into one observable sequence by using the selector function whenever all of the observable sequences have produced an element at a corresponding index.
        /// </summary>

View on GitHub (pinned to 94b5d5ab91)