dotnet/reactive · error · ArgumentNullException

observer

Error message

observer

What it means

AsyncObserver.Scan<TSource>(observer, Func<TSource,TSource,ValueTask<TSource>>) — the async-accumulator observer factory backing the async Scan overload — throws ArgumentNullException because the observer argument was null. The wrapper needs the downstream observer to forward accumulated values and completion signals.

Solutions

  1. Pass a constructed downstream observer as the first argument.
  2. Audit the custom subscription path to ensure the observer is created before AsyncObserver.Scan is invoked.
  3. Route through the public source.Scan(asyncFunc) API to avoid manual observer wiring.

Example fix

// before
var obs = AsyncObserver.Scan<int>((IAsyncObserver<int>)null, async (a, b) => a + b); // throws

// after
var obs = AsyncObserver.Scan(observer, async (a, b) => a + b);
Defensive patterns

Strategy: validation

Validate before calling

if (observer is null) throw new InvalidOperationException("downstream observer required by AsyncObserver.Scan (async)");
var obs = AsyncObserver.Scan(observer, asyncFunc);

Type guard

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

Try / catch

try { var obs = AsyncObserver.Scan(observer, f); }
catch (ArgumentNullException ex) when (ex.ParamName == "observer") { log.LogError("null observer in async scan chain"); throw; }

Prevention

When it happens

Trigger: Calling AsyncObserver.Scan(null, asyncFunc) through internal or custom operator code where the downstream IAsyncObserver<TSource> was null — typically a subscription path that failed to construct or pass the observer.

Common situations: Custom subscription implementations with nullable observer fields; composing observer chains where an earlier stage dropped the observer; direct internal-API testing.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Scan.cs:107

                        }
                    }
                    else
                    {
                        value = x;
                        hasValue = true;
                    }

                    await observer.OnNextAsync(value).ConfigureAwait(false);
                },
                observer.OnErrorAsync,
                observer.OnCompletedAsync
            );
        }

        public static IAsyncObserver<TSource> Scan<TSource>(IAsyncObserver<TSource> observer, Func<TSource, TSource, ValueTask<TSource>> func)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (func == null)
                throw new ArgumentNullException(nameof(func));

            var hasValue = false;
            var value = default(TSource);

            return Create<TSource>(
                async x =>
                {
                    if (hasValue)
                    {
                        try
                        {
                            value = await func(value, x).ConfigureAwait(false);
                        }
                        catch (Exception ex)
                        {
                            await observer.OnErrorAsync(ex).ConfigureAwait(false);

View on GitHub (pinned to 94b5d5ab91)