dotnet/reactive · error · ArgumentNullException

sources

Error message

sources

What it means

The Case(selector, sources, defaultSource) overload throws ArgumentNullException when the sources dictionary is null. The dictionary defines the branch mapping from selector values to observables, so a null map is invalid and rejected at Observable.Imperative.cs:140 before delegation.

Solutions

  1. Construct and pass a populated IDictionary<TValue, IObservable<TResult>> (it may be empty if you always want defaultSource).
  2. Guard the producing method so it returns an empty dictionary rather than null.
  3. Initialize the dictionary before wiring the Case operator.

Example fix

// before
IDictionary<string, IObservable<int>> routes = LoadRoutes(); // may be null
Observable.Case(GetKey, routes, fallback);
// after
IDictionary<string, IObservable<int>> routes = LoadRoutes() ?? new Dictionary<string, IObservable<int>>();
Observable.Case(GetKey, routes, fallback);
Defensive patterns

Strategy: validation

Validate before calling

if (sources is null) throw new ArgumentNullException(nameof(sources));
var result = Observable.Case(selector, sources, defaultSource);

Try / catch

try { var q = Observable.Case(selector, sources, defaultSource); }
catch (ArgumentNullException ex) when (ex.ParamName == "sources") { /* fall back to a defaultSource-only observable */ }

Prevention

When it happens

Trigger: Calling Observable.Case with null in the second parameter (IDictionary<TValue, IObservable<TResult>>), e.g. a dictionary built lazily or loaded from config that was never populated/created.

Common situations: Conditional initialization where the branch table is only built in one environment; a lookup helper returning null on miss; deserialization of route tables that produced null.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Imperative.cs:140

        /// </summary>
        /// <typeparam name="TValue">The type of the value returned by the selector function, used to look up the resulting source.</typeparam>
        /// <typeparam name="TResult">The type of the elements in the result sequence.</typeparam>
        /// <param name="selector">Selector function invoked to determine the source to lookup in the <paramref name="sources"/> dictionary.</param>
        /// <param name="sources">Dictionary of sources to select from based on the <paramref name="selector"/> invocation result.</param>
        /// <param name="defaultSource">Default source to select in case no matching source in <paramref name="sources"/> is found.</param>
        /// <returns>The observable sequence retrieved from the <paramref name="sources"/> dictionary based on the <paramref name="selector"/> invocation result, or <paramref name="defaultSource"/> if no match is found.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="selector"/> or <paramref name="sources"/> or <paramref name="defaultSource"/> is null.</exception>
        public static IObservable<TResult> Case<TValue, TResult>(Func<TValue> selector, IDictionary<TValue, IObservable<TResult>> sources, IObservable<TResult> defaultSource)
            where TValue : notnull
        {
            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 s_impl.Case(selector, sources, defaultSource);
        }

        /// <summary>
        /// Uses <paramref name="selector"/> to determine which source in <paramref name="sources"/> to return, choosing an empty sequence on the specified scheduler if no match is found.
        /// </summary>
        /// <typeparam name="TValue">The type of the value returned by the selector function, used to look up the resulting source.</typeparam>
        /// <typeparam name="TResult">The type of the elements in the result sequence.</typeparam>
        /// <param name="selector">Selector function invoked to determine the source to lookup in the <paramref name="sources"/> dictionary.</param>
        /// <param name="sources">Dictionary of sources to select from based on the <paramref name="selector"/> invocation result.</param>
        /// <param name="scheduler">Scheduler to generate an empty sequence on in case no matching source in <paramref name="sources"/> is found.</param>

View on GitHub (pinned to 94b5d5ab91)