dotnet/reactive · error · ArgumentNullException
Value cannot be null. (Parameter 'first')
Error message
Value cannot be null. (Parameter 'first')
What it means
ArgumentNullException (Parameter 'first'): the two-argument tuple-returning Observable.Zip overload received a null first source. Rx validates both inputs (first, then second) before delegating to the internal implementation, throwing eagerly so callers learn at the call site that a null is not a valid observable sequence.
Solutions
- Ensure the receiver/first argument is a constructed IObservable<T> before calling Zip.
- Return Observable.Empty<T>() or Observable.Never<T>() instead of null from producers.
- Coalesce at the call site: (maybeFirst ?? Observable.Empty<T>()).Zip(second).
- Add caller-side null checks or Debug.Assert to catch producers returning null.
Example fix
// before IObservable<int> first = GetSource(); // may return null var zipped = first.Zip(second); // after var zipped = (first ?? Observable.Empty<int>()).Zip(second);
Defensive patterns
Strategy: validation
Validate before calling
if (first == null) throw new ArgumentNullException(nameof(first)); if (second == null) throw new ArgumentNullException(nameof(second)); // call-site coalescing: var zipped = (first ?? Observable.Empty<TFirst>()).Zip(second ?? Observable.Empty<TSecond>());
Type guard
static bool IsObservable<T>([NotNullWhen(true)] IObservable<T>? o) => o is not null;
if (IsObservable(first) && IsObservable(second)) { /* safe to Zip */ } Try / catch
try
{
var zipped = first.Zip(second);
}
catch (ArgumentNullException ex) when (ex.ParamName == nameof(first))
{
logger.LogError(ex, "first source was null when calling Zip");
} Prevention
- Ensure all producer methods return non-null IObservable<T>; use Empty/Never for absence.
- Never chain operators directly on APIs that may return null — coalesce first.
- Enable C# nullable reference types so null receivers are flagged at compile time.
- In wrappers around legacy code, normalize nulls to Observable.Empty before returning.
When it happens
Trigger: Calling first.Zip(second) with first == null — usually an uninitialized IObservable field, a method returning null instead of Observable.Empty/Never, or chaining Zip on the result of an API that can return null.
Common situations: Legacy wrappers that return null observables, LINQ-style pipelines where an earlier operator returned null instead of a sequence, unit tests stubbing subjects as null.
Related errors
- Value cannot be null. (Parameter 'source14')
- Value cannot be null. (Parameter 'source15')
- Value cannot be null. (Parameter 'source16')
- Value cannot be null. (Parameter 'source1')
- Value cannot be null. (Parameter 'source2')
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/0bf7fc9d6a21df50.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Multiple.Zip.cs:1198
}
#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)View on GitHub (pinned to 94b5d5ab91)