dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'observer')

Error message

Value cannot be null. (Parameter 'observer')

What it means

AsyncObserver.Max<TSource>(IAsyncObserver<TSource>) throws ArgumentNullException('observer') when the downstream observer is null. This factory builds the observer-side Max logic and delegates to Max(observer, Comparer<T>.Default); a null destination observer would have nowhere to emit the result, so it is rejected immediately.

Solutions

  1. Pass a real downstream observer, e.g. from AsyncObserver.Create<T>(onNextAsync, onErrorAsync, onCompletedAsync).
  2. In custom operators, validate the observer before forwarding it into AsyncObserver.Max.
  3. If you want a no-op destination, use a terminal observer (e.g. a TaskCompletionSource-backed observer) instead of null.

Example fix

// before
var maxObserver = AsyncObserver.Max(observer); // observer null

// after
var downstream = AsyncObserver.Create<int>(
    x => default(ValueTask),
    err => default(ValueTask),
    () => default(ValueTask));
var maxObserver = AsyncObserver.Max(downstream);
Defensive patterns

Strategy: validation

Validate before calling

if (observer is null) throw new InvalidOperationException("Max requires a downstream observer");

Type guard

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

Try / catch

try { var maxObs = AsyncObserver.Max(observer); }
catch (ArgumentNullException ex) when (ex.ParamName == "observer") { /* build a terminal observer and retry */ }

Prevention

When it happens

Trigger: Calling AsyncObserver.Max(null) — commonly when hand-writing an operator and forwarding an observer parameter that is itself null, or when a custom IAsyncObservable.SubscribeAsync implementation passes null through.

Common situations: Custom operator authoring where the Create/subscription plumbing drops the observer; test harnesses that pass null observers; observer factories (e.g. from another operator) returning null on error paths.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Max.cs:39

        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (comparer == null)
                throw new ArgumentNullException(nameof(comparer));

            return CreateAsyncObservable<TSource>.From(
                source,
                comparer,
                static (source, comparer, observer) => source.SubscribeSafeAsync(AsyncObserver.Max(observer, comparer)));
        }
    }

    public partial class AsyncObserver
    {
        public static IAsyncObserver<TSource> Max<TSource>(IAsyncObserver<TSource> observer)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));

            return Max(observer, Comparer<TSource>.Default);
        }

        public static IAsyncObserver<TSource> Max<TSource>(IAsyncObserver<TSource> observer, IComparer<TSource> comparer)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (comparer == null)
                throw new ArgumentNullException(nameof(comparer));

            var max = default(TSource);
            var found = false;

            return Create<TSource>(
                async x =>
                {
                    if (found)

View on GitHub (pinned to 94b5d5ab91)