dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'other')

Error message

Value cannot be null. (Parameter 'other')

What it means

QueryablePattern<TSource1,TSource2>.And<TSource3> extends a two-sequence queryable join pattern with a third observable and throws ArgumentNullException when that sequence is null. This variant builds an expression tree (Expression.Call) for the IQbservable provider, which still requires a non-null source sequence. Raised from the public And method.

Solutions

  1. Ensure the third observable is non-null before calling And on the queryable pattern.
  2. Use Observable.Empty<T>()/Observable.Never<T>() as an explicit placeholder sequence.
  3. Fix the query provider/factory so it returns a valid IQbservable instead of null.

Example fix

// before
var qp = pattern.And(thirdSource); // null
// after
var qp = pattern.And(thirdSource ?? Observable.Empty<TThird>());
Defensive patterns

Strategy: validation

Validate before calling

if (source3 == null) throw new ArgumentNullException(nameof(source3));
var qp = queryablePattern.And(source3);

Type guard

bool HasSource3<T3>(IObservable<T3>? o) => o is not null;

Try / catch

try { qp = queryablePattern.And(source3); }
catch (ArgumentNullException ex) when (ex.ParamName == "other") { log.Error("Null third sequence for QueryablePattern.And"); throw; }

Prevention

When it happens

Trigger: Calling queryablePattern.And(null) on a QueryablePattern<TSource1,TSource2>, e.g. a null IObservable<TSource3> from an unassigned variable or a null-returning provider method.

Common situations: IQbservable-based scenarios (e.g. IQbservable over a remote service) where the third sequence comes from a query provider that returned null; mixing System.Reactive and System.Reactive.Providers code paths in tests.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Joins/QueryablePattern.Generated.cs:40

    public class QueryablePattern<TSource1, TSource2> : QueryablePattern
    {
        internal QueryablePattern(Expression expression)
            : base(expression)
        {
        }

        /// <summary>
        /// Creates a pattern that matches when all three observable sequences have an available element.
        /// </summary>
        /// <typeparam name="TSource3">The type of the elements in the third observable sequence.</typeparam>
        /// <param name="other">Observable sequence to match with the two previous sequences.</param>
        /// <returns>Pattern object that matches when all observable sequences have an available element.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="other"/> is null.</exception>
        public QueryablePattern<TSource1, TSource2, TSource3> And<TSource3>(IObservable<TSource3> other)
        {
            if (other == null)
            {
                throw new ArgumentNullException(nameof(other));
            }

            var t = typeof(QueryablePattern<TSource1, TSource2>);
            var m = t.GetMethod(nameof(And)).MakeGenericMethod(typeof(TSource3));
            return new QueryablePattern<TSource1, TSource2, TSource3>(
                Expression.Call(
                    Expression,
                    m,
                    Qbservable.GetSourceExpression(other)
                )
            );
        }

        /// <summary>
        /// Matches when all observable sequences have an available element and projects the elements by invoking the selector function.
        /// </summary>
        /// <typeparam name="TResult">The type of the elements in the result sequence, returned by the selector function.</typeparam>
        /// <param name="selector">Selector that will be invoked for elements in the source sequences.</param>

View on GitHub (pinned to 94b5d5ab91)