dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'comparer')

Error message

Value cannot be null. (Parameter 'comparer')

What it means

AsyncObservable.Max<TSource>(source, IComparer<TSource>) throws ArgumentNullException('comparer') when the comparer argument is null. Unlike the simple overload, this overload requires an explicit IComparer<TSource>; passing null defeats the comparison logic so the library fails fast instead of silently falling back.

Solutions

  1. Pass Comparer<T>.Default if you want default comparison instead of null.
  2. Assign a concrete comparer such as Comparer<int>.Create((a,b) => a.CompareTo(b)) or a custom class implementing IComparer<T>.
  3. Coalesce at the call site: comparer ?? Comparer<T>.Default.

Example fix

// before
var max = AsyncObservable.Max(source, comparer); // comparer is null

// after
var max = AsyncObservable.Max(source, comparer ?? Comparer<int>.Default);
Defensive patterns

Strategy: validation

Validate before calling

if (comparer is null) comparer = Comparer<TSource>.Default; // then call Max(source, comparer)

Type guard

static bool HasComparer<TSource>(IComparer<TSource>? c) => c is not null;

Try / catch

try { var max = AsyncObservable.Max(source, comparer); }
catch (ArgumentNullException ex) when (ex.ParamName == "comparer") { comparer = Comparer<TSource>.Default; /* retry */ }

Prevention

When it happens

Trigger: Calling AsyncObservable.Max(source, null) — e.g. a comparer field/property that was never assigned, a config-selected comparer that resolved to null, or passing null intending to get the default comparer.

Common situations: Migrating code from Comparer-based APIs where null meant 'use default'; conditional comparer selection (e.g. by culture) where the branch didn't assign; reflection or DI lookups that returned null.

Related errors


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

Appendix: source

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

namespace System.Reactive.Linq
{
    public partial class AsyncObservable
    {
        public static IAsyncObservable<TSource> Max<TSource>(IAsyncObservable<TSource> source)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));

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

        public static IAsyncObservable<TSource> Max<TSource>(IAsyncObservable<TSource> source, IComparer<TSource> comparer)
        {
            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);
        }

View on GitHub (pinned to 94b5d5ab91)