dotnet/reactive · error · ArgumentNullException

left

Error message

left

What it means

Join(left, right, leftDurationSelector, rightDurationSelector, resultSelector) throws ArgumentNullException with paramName "left" when the left (outer) observable is null. The operator validates all five arguments eagerly in order, so 'left' failing means the very first argument was null.

Solutions

  1. Ensure the left IAsyncObservable<TLeft> is non-null; substitute AsyncObservable.Empty<TLeft>() when a side is legitimately absent.
  2. Trace the null producer for the left stream and fix it to return an empty observable.
  3. Guard each side with a null check (or ?? Empty) before composing the Join.

Example fix

// before
var joined = left.Join(right, l => lDur, r => rDur, selector); // left is null
// after
var joined = (left ?? AsyncObservable.Empty<Left>())
    .Join(right, l => lDur, r => rDur, selector);
Defensive patterns

Strategy: validation

Validate before calling

if (left is null) throw new ArgumentException("left must be non-null", nameof(left));
// or: left ??= AsyncObservable.Empty<TLeft>();

Type guard

static bool HasLeft<TLeft>(IAsyncObservable<TLeft> o) => o is not null;

Try / catch

try { var joined = left.Join(right, ld, rd, selector); }
catch (ArgumentNullException ex) when (ex.ParamName == "left") { /* ensure left stream is initialized */ }

Prevention

When it happens

Trigger: Calling left.Join(right, ...) via a null extension receiver, or AsyncObservable.Join(null, right, ...) where the left observable came from a null-returning producer.

Common situations: Joining streams where one side is loaded lazily (e.g. a user/session stream that hasn't been created yet), or composing streams from services that may return null before initialization.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Join.cs:17

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT License.
// See the LICENSE file in the project root for more information. 

using System.Collections.Generic;
using System.Reactive.Disposables;
using System.Threading;
using System.Threading.Tasks;

namespace System.Reactive.Linq
{
    public partial class AsyncObservable
    {
        public static IAsyncObservable<TResult> Join<TLeft, TRight, TLeftDuration, TRightDuration, TResult>(this IAsyncObservable<TLeft> left, IAsyncObservable<TRight> right, Func<TLeft, IAsyncObservable<TLeftDuration>> leftDurationSelector, Func<TRight, IAsyncObservable<TRightDuration>> rightDurationSelector, Func<TLeft, TRight, TResult> resultSelector)
        {
            if (left == null)
                throw new ArgumentNullException(nameof(left));
            if (right == null)
                throw new ArgumentNullException(nameof(right));
            if (leftDurationSelector == null)
                throw new ArgumentNullException(nameof(leftDurationSelector));
            if (rightDurationSelector == null)
                throw new ArgumentNullException(nameof(rightDurationSelector));
            if (resultSelector == null)
                throw new ArgumentNullException(nameof(resultSelector));

            return CreateAsyncObservable<TResult>.From(
                left,
                (right, leftDurationSelector, rightDurationSelector, resultSelector),
                static async (left, state, observer) =>
                {
                    var subscriptions = new CompositeAsyncDisposable();

                    var (leftObserver, rightObserver, disposable) = AsyncObserver.Join(observer, subscriptions, state.leftDurationSelector, state.rightDurationSelector, state.resultSelector);

View on GitHub (pinned to 94b5d5ab91)