dotnet/reactive · error · ArgumentNullException
observer
Error message
observer
What it means
The internal AsyncObserver.GroupJoin factory validates its IAsyncObserver<TResult> observer argument and throws ArgumentNullException when it is null. The observer is the downstream sink that receives the joined results, so without it the operator has nothing to notify. This guard is hit when the public GroupJoin operator wires up Create<TResult> or when calling this factory directly.
Solutions
- Ensure the observer passed to AsyncObserver.GroupJoin is the non-null one received from the Create/IAsyncObservable subscription callback.
- If writing a custom operator, pass the observer parameter straight through rather than a captured variable that may be null.
- Add a null check on your own plumbing before delegating to AsyncObserver.GroupJoin.
Example fix
// before
return Create<TResult>(async observer => { var (l, r, d) = AsyncObserver.GroupJoin(null, subs, ld, rd, rs); ... });
// after
return Create<TResult>(async observer => { var (l, r, d) = AsyncObserver.GroupJoin(observer, subs, ld, rd, rs); ... }); Defensive patterns
Strategy: validation
Validate before calling
if (observer == null) throw new ArgumentNullException(nameof(observer));
Type guard
bool HasObserver<T>(IAsyncObserver<T> o) => o is not null;
Try / catch
try { var parts = AsyncObserver.GroupJoin(observer, subs, ld, rd, rs); }
catch (ArgumentNullException ex) when (ex.ParamName == "observer") { /* fix operator plumbing */ } Prevention
- Thread the observer from the Create callback directly into factory calls
- Add Debug.Assert(observer != null) in custom operator plumbing
- Avoid captured observer variables that can be reassigned to null
When it happens
Trigger: Calling AsyncObserver.GroupJoin with null as the first argument; the public operator would only hit this if its Create callback supplied a null observer, which normally indicates a bug in custom operator plumbing rather than user code.
Common situations: Custom operator authoring that forgets to thread the observer through; tests invoking AsyncObserver.GroupJoin directly with placeholder nulls; refactors of the Create pipeline.
Related errors
- Value cannot be null. (Parameter 'observer')
- observer
- throw new ArgumentNullException(nameof(observer));
- func
- observer
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/52e8af11f738d033.
Report an issue: GitHub.
Appendix: source
Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/GroupJoin.cs:50
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;
});
}
}
public partial class AsyncObserver
{
public static (IAsyncObserver<TLeft>, IAsyncObserver<TRight>, IAsyncDisposable) GroupJoin<TLeft, TRight, TLeftDuration, TRightDuration, TResult>(IAsyncObserver<TResult> observer, IAsyncDisposable subscriptions, Func<TLeft, IAsyncObservable<TLeftDuration>> leftDurationSelector, Func<TRight, IAsyncObservable<TRightDuration>> rightDurationSelector, Func<TLeft, IAsyncObservable<TRight>, TResult> resultSelector)
{
if (observer == null)
throw new ArgumentNullException(nameof(observer));
if (subscriptions == null)
throw new ArgumentNullException(nameof(subscriptions));
if (leftDurationSelector == null)
throw new ArgumentNullException(nameof(leftDurationSelector));
if (rightDurationSelector == null)
throw new ArgumentNullException(nameof(rightDurationSelector));
if (resultSelector == null)
throw new ArgumentNullException(nameof(resultSelector));
var gate = new AsyncGate();
var group = new CompositeAsyncDisposable(subscriptions);
var refCount = new RefCountAsyncDisposable(group);
var leftMap = new SortedDictionary<int, IAsyncObserver<TRight>>();
var rightMap = new SortedDictionary<int, TRight>();
var leftId = default(int);View on GitHub (pinned to 94b5d5ab91)