dotnet/reactive · error · ArgumentNullException

observer

Error message

observer

What it means

AsyncObserver.Distinct throws ArgumentNullException when the downstream IAsyncObserver<TSource> is null. This observer-facing factory is used inside custom operator implementations and delegates to Distinct(observer, EqualityComparer<TSource>.Default). The null check happens before any observer chain is built.

Solutions

  1. Pass the real downstream observer you received in your subscribe delegate.
  2. Add your own ArgumentNullException guards at the entry of your custom operator.
  3. Prefer the built-in Create(source, observer) operator helper over manual observer construction.

Example fix

// before
AsyncObserver.Distinct(null); // throws
// after
return source.SubscribeSafeAsync(AsyncObserver.Distinct(downstreamObserver));
Defensive patterns

Strategy: validation

Validate before calling

if (observer is null) throw new ArgumentNullException(nameof(observer));
var distinctObserver = AsyncObserver.Distinct(observer);

Type guard

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

Try / catch

try
{
    var o = AsyncObserver.Distinct(observer);
}
catch (ArgumentNullException ex) when (ex.ParamName == "observer")
{
    logger.LogError(ex, "Distinct observer factory got null observer");
    throw;
}

Prevention

When it happens

Trigger: Calling AsyncObserver.Distinct(null) from a custom subscribe path or forwarding a null observer argument.

Common situations: Hand-written operators where the observer passed into Create's subscribe delegate is misused or lost; wiring observers across layers where one layer passes null.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Distinct.cs:38

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

            return Create(
                source,
                comparer,
                static (source, comparer, observer) => source.SubscribeSafeAsync(AsyncObserver.Distinct(observer, comparer)));
        }
    }

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

            return Distinct(observer, EqualityComparer<TSource>.Default);
        }

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

            var set = new HashSet<TSource>(comparer);

            return Create<TSource>(
                async x =>
                {
                    var added = false;

View on GitHub (pinned to 94b5d5ab91)