dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'observer')

Error message

Value cannot be null. (Parameter 'observer')

What it means

ArgumentNullException raised by the AsyncObserver.WithLatestFrom factory overload because the target IAsyncObserver<TResult> is null. This overload creates the paired (first, second) observers that feed a WithLatestFrom pipeline and validates the downstream observer first.

Solutions

  1. Pass a valid downstream IAsyncObserver<TResult>
  2. Check the factory/subscription code that was supposed to supply the observer
  3. Guard the call site before invoking the observer factory

Example fix

// before
var (obs1, obs2) = AsyncObserver.WithLatestFrom<int, int, string>(null, (x, y) => $"{x}:{y}");
// after
var downstream = GetObserver(); // must be non-null
var (obs1, obs2) = AsyncObserver.WithLatestFrom<int, int, string>(downstream, (x, y) => $"{x}:{y}");
Defensive patterns

Strategy: validation

Validate before calling

if (observer is null) throw new InvalidOperationException("Downstream observer must be created before WithLatestFrom observers can be attached.");

Type guard

bool HasObserver<T>(IAsyncObserver<T>? o) => o is not null;

Try / catch

try { var pair = AsyncObserver.WithLatestFrom<T1,T2,TR>(observer, selector); }
catch (ArgumentNullException ex) when (ex.ParamName == "observer")
{
    // observer pipeline mis-wired: log and rethrow with context
    throw new InvalidOperationException("WithLatestFrom requires a subscribed downstream observer.", ex);
}

Prevention

When it happens

Trigger: Calling AsyncObserver.WithLatestFrom<TFirst,TSecond,TResult>(null, resultSelector) with a sync Func selector, e.g. when building custom operator plumbing and the downstream observer is null.

Common situations: Composing custom operators where the observer comes from a CreateAsyncObservable callback whose argument was null; passing a not-yet-initialized observer field.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/WithLatestFrom.cs:92

                {
                    var (firstObserver, secondObserver) = AsyncObserver.WithLatestFrom(observer, state.resultSelector);

                    // REVIEW: Consider concurrent subscriptions.

                    var firstSubscription = await first.SubscribeSafeAsync(firstObserver).ConfigureAwait(false);
                    var secondSubscription = await state.second.SubscribeSafeAsync(secondObserver).ConfigureAwait(false);

                    return StableCompositeAsyncDisposable.Create(firstSubscription, secondSubscription);
                });
        }
    }

    public partial class AsyncObserver
    {
        public static (IAsyncObserver<TFirst>, IAsyncObserver<TSecond>) WithLatestFrom<TFirst, TSecond, TResult>(IAsyncObserver<TResult> observer, Func<TFirst, TSecond, TResult> resultSelector)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (resultSelector == null)
                throw new ArgumentNullException(nameof(resultSelector));

            return WithLatestFrom<TFirst, TSecond, TResult>(observer, (x, y) => new ValueTask<TResult>(resultSelector(x, y)));
        }

        public static (IAsyncObserver<TFirst>, IAsyncObserver<TSecond>) WithLatestFrom<TFirst, TSecond, TResult>(IAsyncObserver<TResult> observer, Func<TFirst, TSecond, ValueTask<TResult>> resultSelector)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (resultSelector == null)
                throw new ArgumentNullException(nameof(resultSelector));

            var gate = new AsyncGate();

            async ValueTask OnErrorAsync(Exception ex)
            {
                using (await gate.LockAsync().ConfigureAwait(false))

View on GitHub (pinned to 94b5d5ab91)