dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'keySelector')

Error message

Value cannot be null. (Parameter 'keySelector')

What it means

In ToDictionary(source, keySelector, valueSelector), the keySelector null check at line 17 throws ArgumentNullException. The key selector produces each dictionary key during aggregation; a null selector would crash mid-stream, so the library rejects it up front. Either selector being null is invalid.

Solutions

  1. Pass a non-null Func<TSource, TKey> key selector, e.g. x => x.Id.
  2. Check that the delegate-producing code (DI, reflection, config) actually returns a function.
  3. Validate both selectors before composing the query.

Example fix

// before
Func<Order, int> keySel = null;
var dict = orders.ToDictionary(keySel, o => o.Total);
// after
var dict = orders.ToDictionary(o => o.Id, o => o.Total);
Defensive patterns

Strategy: validation

Validate before calling

if (keySelector is null) throw new ArgumentException("keySelector required");
var dict = source.ToDictionary(keySelector, valueSelector);

Type guard

static bool HasKeySelector<T, K>(Func<T, K> k) => k is not null;

Try / catch

try { var d = source.ToDictionary(ks, vs); }
catch (ArgumentNullException ex) when (ex.ParamName == "keySelector") { /* supply selector */ }

Prevention

When it happens

Trigger: Calling source.ToDictionary(null, valueSelector); passing a selector field that was never assigned.

Common situations: Dynamic query builders where the selector delegate comes from reflection or config and fails to resolve; refactors that renamed a method used as the selector, leaving a null delegate in a dictionary of factories.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/ToDictionary.cs:17

// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT License.
// See the LICENSE file in the project root for more information. 

using System.Collections.Generic;
using System.Threading.Tasks;

namespace System.Reactive.Linq
{
    public partial class AsyncObservable
    {
        public static IAsyncObservable<IDictionary<TKey, TValue>> ToDictionary<TSource, TKey, TValue>(this IAsyncObservable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TValue> valueSelector)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (keySelector == null)
                throw new ArgumentNullException(nameof(keySelector));
            if (valueSelector == null)
                throw new ArgumentNullException(nameof(valueSelector));

            return CreateAsyncObservable<IDictionary<TKey, TValue>>.From(
                source,
                (keySelector, valueSelector),
                static (source, state, observer) => source.SubscribeSafeAsync(AsyncObserver.ToDictionary(observer, state.keySelector, state.valueSelector)));
        }

        public static IAsyncObservable<IDictionary<TKey, TValue>> ToDictionary<TSource, TKey, TValue>(this IAsyncObservable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TValue> valueSelector, IEqualityComparer<TKey> comparer)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (keySelector == null)
                throw new ArgumentNullException(nameof(keySelector));
            if (valueSelector == null)
                throw new ArgumentNullException(nameof(valueSelector));
            if (comparer == null)

View on GitHub (pinned to 94b5d5ab91)