dotnet/reactive · error · ArgumentNullException
resultSelector
Error message
resultSelector
What it means
The Join operator throws ArgumentNullException because the resultSelector delegate was null. The result selector combines each matched (left, right) pair into a TResult and is essential to the operator's output. It is validated up front so the failure occurs at operator construction, not on first match.
Solutions
- Pass a non-null Func<TLeft, TRight, TResult> as the fifth argument.
- If you truly need pass-through, use (l, r) => (l, r) tuple selection instead of null.
- Check any wrapper/helper API that forwards selectors and ensure defaults are non-null.
Example fix
// before var result = left.Join(right, l => l.Duration, r => r.Duration, null); // after var result = left.Join(right, l => l.Duration, r => r.Duration, (l, r) => new Match(l, r));
Defensive patterns
Strategy: validation
Validate before calling
if (resultSelector is null) throw new ArgumentException("resultSelector is required", nameof(resultSelector)); Type guard
bool SelectorSet<TL, TR, TRes>(Func<TL, TR, TRes>? f) => f is not null;
Try / catch
try { var joined = left.Join(right, lds, rds, rs); }
catch (ArgumentNullException ex) when (ex.ParamName == "resultSelector") { /* supply a selector, e.g. tuple projection */ } Prevention
- Always provide a projection lambda; use (l, r) => (l, r) for pass-through.
- Avoid nullable Func fields for selectors; construct them at the call site.
- Review copy-pasted Join calls for removed lambda arguments.
When it happens
Trigger: Calling left.Join(right, leftDurationSelector, rightDurationSelector, null) — commonly caused by forgetting the final lambda or a null delegate field.
Common situations: Copy-paste of Join calls where the result lambda was removed, or generic helper methods that forward a null selector parameter.
Related errors
- leftDurationSelector
- 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/66b9e8742705fcad.
Report an issue: GitHub.
Appendix: source
Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Join.cs:25
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);
await subscriptions.AddAsync(rightSubscription).ConfigureAwait(false);
return disposable;
});View on GitHub (pinned to 94b5d5ab91)