dotnet/reactive · error · ArgumentNullException

keySelector

Error message

keySelector

What it means

Observable.MaxBy throws ArgumentNullException when the keySelector delegate passed to it is null. Rx operators validate all arguments eagerly at call time, before returning the observable, so the exception is thrown synchronously rather than surfaced through the subscription. keySelector is required because MaxBy must compute a comparison key for each element to find the maximum elements.

Solutions

  1. Pass a valid non-null lambda or method group as keySelector, e.g. source.MaxBy(x => x.Priority).
  2. If the selector comes from a variable, check it for null before calling MaxBy and throw a descriptive error or substitute a default selector.
  3. Fix the initialization of the field/property/config value that supplies the selector so it is never null.

Example fix

// before
Func<Order, int> selector = config?.OrderSelector;
var max = orders.MaxBy(selector); // NRE/ArgumentNullException if config is null
// after
var max = orders.MaxBy(o => o.Total); // or: orders.MaxBy(selector ?? (o => o.Total))
Defensive patterns

Strategy: validation

Validate before calling

if (source == null) throw new ArgumentNullException(nameof(source));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
var max = source.MaxBy(keySelector);

Type guard

bool HasSelector<T, TKey>(Func<T, TKey> selector) => selector is not null;

Try / catch

try { var max = source.MaxBy(keySelector); }
catch (ArgumentNullException ex) when (ex.ParamName == "keySelector") { /* supply default selector or log */ }

Prevention

When it happens

Trigger: Calling Observable.MaxBy(source, null) — the two-argument overload (IObservable<TSource>, Func<TSource,TKey>) at Observable.Aggregates.cs:1556 with a null second argument, e.g. MaxBy(x => someSelector) where someSelector is an uninitialized field or a failed lookup.

Common situations: Storing the key selector in a field or dictionary that was never initialized; dynamically resolving the selector from config/dependency injection and getting null; refactoring code so the lambda was replaced by a nullable delegate variable.

Related errors


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

Appendix: source

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

        /// Returns the elements in an observable sequence with the maximum key value.
        /// </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>
        /// <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"/> 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)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

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

            return s_impl.MaxBy(source, keySelector);
        }

        /// <summary>
        /// Returns the elements in an observable sequence with the maximum key value according to the specified comparer.
        /// </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)
        {

View on GitHub (pinned to 94b5d5ab91)