dotnet/reactive · error · ArgumentNullException
leftDurationSelector
Error message
leftDurationSelector
What it means
The Join operator throws ArgumentNullException because the leftDurationSelector delegate was null. This delegate maps each left item to the observable that governs how long it stays joinable; without it the operator cannot function, so it is validated eagerly. The library throws immediately at operator creation rather than during subscription.
Solutions
- Supply a non-null Func<TLeft, IAsyncObservable<TLeftDuration>> as the third argument.
- Verify the delegate variable is assigned before the Join call (guard with a null check or default lambda).
- If durations are irrelevant, return an empty/never-completing observable from the selector as appropriate instead of passing null.
Example fix
// before var result = left.Join(right, null, r => r.Duration, (l, r) => (l, r)); // after var result = left.Join(right, l => AsyncObservable.Empty<TLeftDuration>(), r => r.Duration, (l, r) => (l, r));
Defensive patterns
Strategy: validation
Validate before calling
if (leftDurationSelector is null) throw new ArgumentException("leftDurationSelector is required", nameof(leftDurationSelector)); Type guard
bool SelectorSet<TL, TD>(Func<TL, IAsyncObservable<TD>>? f) => f is not null;
Try / catch
try { var joined = left.Join(right, lds, rds, rs); }
catch (ArgumentNullException ex) when (ex.ParamName == "leftDurationSelector") { /* supply a default selector and retry */ } Prevention
- Pass lambdas inline to Join rather than via nullable delegate fields.
- Assign DI-injected delegate dependencies in the constructor, not lazily.
- Use default selector helpers (e.g. _ => AsyncObservable.Empty<T>()) when a duration is irrelevant.
When it happens
Trigger: Calling left.Join(right, null, rightDurationSelector, resultSelector) — typically the lambda was omitted or a delegate variable was null.
Common situations: Storing selector delegates in fields/dependency-injected services that were never assigned, or calling via reflection/dynamic wrappers that pass null defaults.
Related errors
- resultSelector
- rightDurationSelector
- throw new ArgumentNullException(nameof(throttleSelector));
- action (Value cannot be null)
- Argument cannot be null (Parameter name: actionAsync)
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/9d7c67a8feaa28ba.
Report an issue: GitHub.
Appendix: source
Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Join.cs:21
// 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);
var leftSubscription = await left.SubscribeSafeAsync(leftObserver).ConfigureAwait(false);
await subscriptions.AddAsync(leftSubscription).ConfigureAwait(false);
var rightSubscription = await state.right.SubscribeSafeAsync(rightObserver).ConfigureAwait(false);View on GitHub (pinned to 94b5d5ab91)