dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'keySelector')

Error message

Value cannot be null. (Parameter 'keySelector')

What it means

The comparer overload of Observable.MaxBy throws ArgumentNullException when the keySelector delegate is null. As with all Rx operators, validation happens synchronously at call time via ArgumentNullException(nameof(keySelector)). keySelector defines the key used with the supplied IComparer<TKey> to rank elements.

Solutions

  1. Pass a concrete key-selection lambda, e.g. source.MaxBy(x => x.Age, comparer).
  2. Guard the delegate before the call: if (keySelector == null) throw new InvalidOperationException("keySelector required").
  3. Correct the registration/factory that was supposed to produce the keySelector delegate.

Example fix

// before
obs.MaxBy(keyExtractor, myComparer); // keyExtractor is null
// after
obs.MaxBy(item => item.Key, myComparer);
Defensive patterns

Strategy: validation

Validate before calling

if (source == null || keySelector == null || comparer == null)
    throw new ArgumentException("MaxBy requires non-null source, keySelector, and comparer");
var r = source.MaxBy(keySelector, comparer);

Type guard

bool ValidForMaxBy<T, TKey>(IObservable<T> s, Func<T, TKey> sel) => s is not null && sel is not null;

Try / catch

try { var r = source.MaxBy(keySelector, comparer); }
catch (ArgumentNullException ex) when (ex.ParamName == "keySelector") { /* provide fallback selector */ }

Prevention

When it happens

Trigger: Calling Observable.MaxBy(source, null, comparer) — the three-argument overload at Observable.Aggregates.cs:1582 with a null keySelector while supplying a comparer.

Common situations: Resolver/DI container returning a null delegate for a registered key-extraction function; a generic helper method whose keySelector parameter was forwarded as null; a nullable Func field not yet initialized at call time.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Aggregates.cs:1582

        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <typeparam name="TKey">The type of the key computed for each element in the source sequence.</typeparam>
        /// <param name="source">An observable sequence to get the maximum elements for.</param>
        /// <param name="keySelector">Key selector function.</param>
        /// <param name="comparer">Comparer used to compare key values.</param>
        /// <returns>An observable sequence containing a list of zero or more elements that have a maximum key value.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="keySelector"/> or <paramref name="comparer"/> is null.</exception>
        /// <remarks>The return type of this operator differs from the corresponding operator on IEnumerable in order to retain asynchronous behavior.</remarks>
        public static IObservable<IList<TSource>> MaxBy<TSource, TKey>(this IObservable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            if (keySelector == null)
            {
                throw new ArgumentNullException(nameof(keySelector));
            }

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

            return s_impl.MaxBy(source, keySelector, comparer);
        }

        #endregion

        #region + Min +

        /// <summary>
        /// Returns the minimum element in an observable sequence.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>

View on GitHub (pinned to 94b5d5ab91)