dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'source4')

Error message

Value cannot be null. (Parameter 'source4')

What it means

The four-source Observable.Zip overload throws ArgumentNullException when resultSelector is null. All four source sequences passed validation; the combining Func<TSource1,TSource2,TSource3,TSource4,TResult> is null. Provide a non-null selector that maps the four latest element tuples to the result.

Solutions

  1. Pass a valid lambda or method group as the four-argument result selector.
  2. Default the selector when it is dynamically resolved (selector ?? ((a,b,c,d) => default(TResult))).
  3. Assert delegate fields are assigned before using them in zip construction.

Example fix

// before
Observable.Zip(a, b, c, d, combine); // combine == null
// after
Func<int, int, int, int, int> combine = combineFn ?? ((w, x, y, z) => w + x + y + z);
Observable.Zip(a, b, c, d, combine);
Defensive patterns

Strategy: validation

Validate before calling

if (resultSelector == null) throw new InvalidOperationException("resultSelector must be provided");

Type guard

bool IsValid<T1,T2,T3,T4,TResult>(Func<T1,T2,T3,T4,TResult> f) => f is not null;

Try / catch

try { return Observable.Zip(a, b, c, d, resultSelector); }
catch (ArgumentNullException ex) when (ex.ParamName == "resultSelector") { /* assign default selector and retry */ }

Prevention

When it happens

Trigger: Calling Observable.Zip(source1, source2, source3, source4, resultSelector) with a null selector delegate.

Common situations: Selector retrieved from a strategy/registry lookup that returned null; passing a delegate field that was never assigned in a partially-constructed object.

Related errors


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

Appendix: source

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

        {
            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 (source4 == null)
            {
                throw new ArgumentNullException(nameof(source4));
            }

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

            return s_impl.Zip(source1, source2, source3, source4, 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>
        /// <typeparam name="TSource1">The type of the elements in the first source sequence.</typeparam>
        /// <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="TSource4">The type of the elements in the fourth source sequence.</typeparam>
        /// <typeparam name="TSource5">The type of the elements in the fifth source sequence.</typeparam>

View on GitHub (pinned to 94b5d5ab91)