dotnet/reactive · error · ArgumentNullException

resultSelector

Error message

resultSelector

What it means

GroupJoin validates every delegate argument before doing any work and throws ArgumentNullException when the resultSelector delegate is null. The resultSelector combines each left value with the matching right observable into the final result, so the operator cannot proceed without it. This fail-fast guard surfaces the programming mistake at the call site instead of failing later inside the subscription.

Solutions

  1. Pass a non-null resultSelector delegate that maps (TLeft, IAsyncObservable<TRight>) to TResult.
  2. Check argument order — ensure the lambda lands in the fifth parameter position, not shifted by a missing duration selector.
  3. If the selector is conditionally supplied, default it to a valid fallback function before calling GroupJoin.

Example fix

// before
var joined = AsyncObservable.GroupJoin(left, right, ld, rd, (Func<Left, IObservable<Right>, Result>)null);
// after
var joined = AsyncObservable.GroupJoin(left, right, ld, rd, (l, rObs) => new Result(l, rObs));
Defensive patterns

Strategy: validation

Validate before calling

if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));
// or coerce: resultSelector ??= (l, rObs) => Result.Default(l, rObs);

Type guard

bool HasResultSelector(Func<L, IAsyncObservable<R>, T> f) => f is not null;

Try / catch

try { var joined = AsyncObservable.GroupJoin(left, right, ld, rd, resultSelector); }
catch (ArgumentNullException ex) when (ex.ParamName == "resultSelector") { /* supply default selector and retry */ }

Prevention

When it happens

Trigger: Calling AsyncObservable.GroupJoin(left, right, leftDurationSelector, rightDurationSelector, resultSelector) with a null fifth argument, e.g. passing an uninitialized Func<TLeft, IAsyncObservable<TRight>, TResult> or a misordered argument list that shifts null into the resultSelector position.

Common situations: Builders that assemble operator arguments from nullable configuration or DI-resolved delegates; refactors that changed the selector signature leaving a stale null lambda; conditional logic that assigns the selector only in some branches.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupJoin.cs:26

using System.Threading;
using System.Threading.Tasks;

namespace System.Reactive.Linq
{
    public partial class AsyncObservable
    {
        public static IAsyncObservable<TResult> GroupJoin<TLeft, TRight, TLeftDuration, TRightDuration, TResult>(this IAsyncObservable<TLeft> left, IAsyncObservable<TRight> right, Func<TLeft, IAsyncObservable<TLeftDuration>> leftDurationSelector, Func<TRight, IAsyncObservable<TRightDuration>> rightDurationSelector, Func<TLeft, IAsyncObservable<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 Create<TResult>(async observer =>
            {
                var subscriptions = new CompositeAsyncDisposable();

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

                var leftSubscription = await left.SubscribeSafeAsync(leftObserver).ConfigureAwait(false);
                await subscriptions.AddAsync(leftSubscription).ConfigureAwait(false);

                var rightSubscription = await right.SubscribeSafeAsync(rightObserver).ConfigureAwait(false);
                await subscriptions.AddAsync(rightSubscription).ConfigureAwait(false);

                return disposable;
            });
        }
    }

View on GitHub (pinned to 94b5d5ab91)