dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'observer')

Error message

Value cannot be null. (Parameter 'observer')

What it means

AsyncObserver.SequenceEqual<TSource>(observer) builds the pair of observers that feed a SequenceEqual comparison; it validates the downstream bool observer first. A null observer throws ArgumentNullException with parameter 'observer'.

Solutions

  1. Pass the actual downstream IAsyncObserver<bool> received in your subscription callback.
  2. Verify you are using Create/CreateAsyncObservable correctly so observer is always provided by the framework.
  3. Add a null guard before composing the observer pair.
  4. Check you are not confusing the observer-factory overloads with the observable-factory SequenceEqual overloads.

Example fix

// before
return Create<bool>(observer => source.SubscribeSafeAsync(AsyncObserver.SequenceEqual<int>(null)));
// after
return Create<bool>(observer => source.SubscribeSafeAsync(AsyncObserver.SequenceEqual<int>(observer)));
Defensive patterns

Strategy: validation

Validate before calling

if (observer is null) throw new ArgumentNullException(nameof(observer));

Type guard

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

Try / catch

try { var pair = AsyncObserver.SequenceEqual<TSource>(observer); }
catch (ArgumentNullException ex) when (ex.ParamName == "observer") { /* fix composition — observer must come from Subscribe callback */ }

Prevention

When it happens

Trigger: Calling AsyncObserver.SequenceEqual<TSource>(null) when manually composing observer pipelines (custom Create implementations) without a downstream observer.

Common situations: Hand-rolled operator composition where the downstream observer variable is null because an outer Subscribe callback received/created it incorrectly; misused CreateAsyncObservable.From overloads.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/SequenceEqual.cs:75

                    var firstTask = first.SubscribeSafeAsync(firstObserver);
                    var secondTask = state.second.SubscribeSafeAsync(secondObserver);

                    // REVIEW: Consider concurrent subscriptions.

                    var d1 = await firstTask.ConfigureAwait(false);
                    var d2 = await secondTask.ConfigureAwait(false);

                    return StableCompositeAsyncDisposable.Create(d1, d2);
                });
        }
    }

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

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

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

            var gate = new AsyncGate();

            var queueLeft = new Queue<TSource>();
            var queueRight = new Queue<TSource>();
            var doneLeft = false;
            var doneRight = false;

View on GitHub (pinned to 94b5d5ab91)