dotnet/reactive · error · ArgumentNullException

throw new ArgumentNullException(nameof(observer));

Error message

throw new ArgumentNullException(nameof(observer));

What it means

The Retry operator requires a downstream observer to feed retried source notifications to. System.Reactive.Async throws ArgumentNullException immediately when the observer argument is null, before any subscription work starts, to fail fast instead of crashing later inside Catch/Repeat.

Solutions

  1. Pass a non-null IAsyncObserver<TSource> as the observer argument, e.g. create one via AsyncObserver.Create or chain from an existing pipeline.
  2. Check the code that produces the observer — it is returning null; fix the factory/container registration.
  3. If the observer is optional by design, guard the call site before invoking Retry.

Example fix

// before
var (obs, disp) = AsyncObserver.Retry(observer, source); // observer is null
// after
if (observer == null) observer = AsyncObserver.Create<TSource>(...);
var (obs, disp) = AsyncObserver.Retry(observer, source);
Defensive patterns

Strategy: validation

Validate before calling

if (observer == null) throw new InvalidOperationException("observer must be constructed before Retry");
var (obs, disp) = AsyncObserver.Retry(observer, source);

Type guard

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

Try / catch

try { var (obs, disp) = AsyncObserver.Retry(observer, source); }
catch (ArgumentNullException ex) when (ex.ParamName == "observer") { /* fix wiring; use default observer */ }

Prevention

When it happens

Trigger: Calling AsyncObserver.Retry<TSource>(null, someSource) — passing a null IAsyncObserver<TSource> as the first argument.

Common situations: Observers produced by a factory method or DI container that returned null; refactoring that removed observer construction but kept the Retry call; conditional observer wiring where a variable is unassigned on some paths.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Retry.cs:55

                source,
                retryCount,
                static async (source, retryCount, observer) =>
                {
                    var (sink, inner) = AsyncObserver.Retry(observer, source, retryCount);

                    var subscription = await source.SubscribeSafeAsync(sink).ConfigureAwait(false);

                    return StableCompositeAsyncDisposable.Create(subscription, inner);
                });
        }
    }

    public partial class AsyncObserver
    {
        public static (IAsyncObserver<TSource>, IAsyncDisposable) Retry<TSource>(IAsyncObserver<TSource> observer, IAsyncObservable<TSource> source)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (source == null)
                throw new ArgumentNullException(nameof(source));

            return Catch(observer, Repeat(source).GetEnumerator());
        }

        public static (IAsyncObserver<TSource>, IAsyncDisposable) Retry<TSource>(IAsyncObserver<TSource> observer, IAsyncObservable<TSource> source, int retryCount)
        {
            if (observer == null)
                throw new ArgumentNullException(nameof(observer));
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (retryCount < 0)
                throw new ArgumentOutOfRangeException(nameof(retryCount));

            return Catch(observer, Enumerable.Repeat(source, retryCount).GetEnumerator());
        }
    }

View on GitHub (pinned to 94b5d5ab91)