dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'selector')

Error message

Value cannot be null. (Parameter 'selector')

What it means

Thrown by the Case operator (Case(selector, sources, defaultSource), reached via the scheduler overload too) when the selector function is null. Case calls selector per subscription to pick which source observable from the dictionary to subscribe to; a null selector would crash at subscribe time, so it is validated when the operator is built.

Solutions

  1. Pass a concrete selector, e.g. () => settings.Mode or () => key.
  2. Coalesce with a default key function: selector ?? (() => defaultKey).
  3. Validate the delegate at construction of your pipeline, before composing Case.

Example fix

// before
var stream = Case(modeSelector, sources, defaultSource); // modeSelector null
// after
var sel = modeSelector ?? (() => ModeKey.Default);
var stream = Case(sel, sources, defaultSource);
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

bool HasSelector<TValue>(Func<TValue>? f) => f is not null;

Try / catch

try { var s = Case(selector, sources, defaultSource); }
catch (ArgumentNullException ex) when (ex.ParamName == "selector") { /* provide selector */ }

Prevention

When it happens

Trigger: Calling Case<TValue, TResult>(selector, sources, scheduler) or Case(selector, sources, defaultSource) with selector == null, e.g. a delegate resolved from DI/config that is null.

Common situations: Key-selector delegates stored in nullable fields; switching logic where the mode function was removed in a refactor; test doubles returning null selectors.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/Case.cs:20

// 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.Reactive.Concurrency;
using System.Threading.Tasks;

namespace System.Reactive.Linq
{
    public partial class AsyncObservable
    {
        public static IAsyncObservable<TResult> Case<TValue, TResult>(Func<TValue> selector, IDictionary<TValue, IAsyncObservable<TResult>> sources) => Case(selector, sources, Empty<TResult>());

        public static IAsyncObservable<TResult> Case<TValue, TResult>(Func<TValue> selector, IDictionary<TValue, IAsyncObservable<TResult>> sources, IAsyncScheduler scheduler) => Case(selector, sources, Empty<TResult>(scheduler));

        public static IAsyncObservable<TResult> Case<TValue, TResult>(Func<TValue> selector, IDictionary<TValue, IAsyncObservable<TResult>> sources, IAsyncObservable<TResult> defaultSource)
        {
            if (selector == null)
                throw new ArgumentNullException(nameof(selector));
            if (sources == null)
                throw new ArgumentNullException(nameof(sources));
            if (defaultSource == null)
                throw new ArgumentNullException(nameof(defaultSource));

            return Create<TResult>(observer =>
            {
                var source = default(IAsyncObservable<TResult>);

                try
                {
                    var value = selector();

                    if (!sources.TryGetValue(value, out source))
                    {
                        source = defaultSource;
                    }
                }

View on GitHub (pinned to 94b5d5ab91)