dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'second')

Error message

Value cannot be null. (Parameter 'second')

What it means

System.Reactive's public Zip<TFirst, TSecond> extension method explicitly validates its arguments before delegating to the internal implementation, throwing ArgumentNullException when the second source sequence is null. Rx operators require non-null sources because the internal pipeline calls Subscribe on every sequence and cannot recover from a null. This is an immediate, fail-fast argument validation, not a runtime failure of the query.

Solutions

  1. Ensure the second observable is assigned before calling Zip (check for uninitialized/failed factory results).
  2. Substitute Observable.Empty<TSecond>() or Observable.Never<TSecond>() when 'no sequence' is the intended semantic.
  3. Wrap the call site in a null check and throw a meaningful domain-specific error or log which source was missing.

Example fix

// before
var zipped = xs.Zip(ys); // ys is null
// after
var zipped = xs.Zip(ys ?? Observable.Empty<int>());
Defensive patterns

Strategy: validation

Validate before calling

if (first == null) throw new ArgumentNullException(nameof(first));
if (second == null) throw new ArgumentNullException(nameof(second));
var zipped = first.Zip(second);

Type guard

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

Try / catch

try { var zipped = first.Zip(second); }
catch (ArgumentNullException ex) when (ex.ParamName == "second") { /* log and use fallback source */ }

Prevention

When it happens

Trigger: Calling Observable.Zip(first, null) or IObservable.Zip(first, second) where the second IObservable<TSecond> variable is null (e.g. an uninitialized field, a failed lookup, or a method returned null instead of Observable.Empty).

Common situations: Passing a sequence obtained from a dictionary lookup or optional dependency that turned out to be null; a factory method returning null instead of an empty observable; refactoring that removed a default sequence assignment.

Related errors


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

Appendix: source

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

#pragma warning disable CA1711 // (Don't use Ex suffix.) This has been a public type for many years, so we can't rename it now.
    public static partial class ObservableEx
#pragma warning restore CA1711
    {
        /// <summary>
        /// Merges the specified observable sequences into one observable sequence of tuple values whenever all of the observable sequences have produced an element at a corresponding index.
        /// </summary>
        /// <typeparam name="TFirst">The type of the elements in the first source sequence.</typeparam>
        /// <typeparam name="TSecond">The type of the elements in the second source sequence.</typeparam>
        /// <param name="first">First observable source.</param>
        /// <param name="second">Second observable source.</param>
        /// <returns>An observable sequence containing the result of combining elements of the sources using tuple values.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="first"/> or <paramref name="second"/> is null.</exception>
        public static IObservable<(TFirst First, TSecond Second)> Zip<TFirst, TSecond>(this IObservable<TFirst> first, IObservable<TSecond> second)
        {
            if (first == null)
                throw new ArgumentNullException(nameof(first));
            if (second == null)
                throw new ArgumentNullException(nameof(second));

            return s_impl.Zip(first, second);
        }

        /// <summary>
        /// Merges the specified observable sequences into one observable sequence of tuple values whenever all of the observable sequences have produced an element at a corresponding index.
        /// </summary>
        /// <typeparam name="TFirst">The type of the elements in the first source sequence.</typeparam>
        /// <typeparam name="TSecond">The type of the elements in the second source sequence.</typeparam>
        /// <typeparam name="TThird">The type of the elements in the third source sequence.</typeparam>
        /// <param name="first">First observable source.</param>
        /// <param name="second">Second observable source.</param>
        /// <param name="third">Third observable source.</param>
        /// <returns>An observable sequence containing the result of combining elements of the sources using tuple values.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="first"/> or <paramref name="second"/> or <paramref name="third"/> is null.</exception>
        public static IObservable<(TFirst First, TSecond Second, TThird Third)> Zip<TFirst, TSecond, TThird>(this IObservable<TFirst> first, IObservable<TSecond> second, IObservable<TThird> third)
        {
            if (first == null)

View on GitHub (pinned to 94b5d5ab91)