dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'taskSelector')

Error message

Value cannot be null. (Parameter 'taskSelector')

What it means

The task-based SelectMany overload requires a non-null taskSelector delegate that maps each source element to a Task<TTaskResult>. A null taskSelector would make it impossible to produce the inner sequences, so the operator throws ArgumentNullException synchronously before returning an observable. Rx throws these argument-null exceptions eagerly at call time rather than delivering them through the stream.

Solutions

  1. Pass a non-null lambda or method group as the taskSelector argument
  2. Check any Func field/property feeding the call and initialize it before use
  3. If the selector is optional, branch: use a different overload or Observable.Empty when no selector is available
  4. Wrap the operator call in a guard that throws a descriptive exception naming the missing selector

Example fix

// before
Func<int, Task<string>>? loader = _config.EnableFetch ? FetchAsync : null;
var result = source.SelectMany(loader, (x, r) => r); // throws when null

// after
var result = _config.EnableFetch
    ? source.SelectMany(FetchAsync, (x, r) => r)
    : Observable.Empty<string>();
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

static bool HasSelector<TSource, TRes>(Func<TSource, Task<TRes>>? selector) => selector is not null;

Try / catch

try { var result = source.SelectMany(taskSelector, resultSelector); }
catch (ArgumentNullException ex) when (ex.ParamName == "taskSelector") { /* supply a default selector or skip the operation */ }

Prevention

When it happens

Trigger: Calling Observable.SelectMany(source, taskSelector, resultSelector) where taskSelector is null - typically a null lambda variable, an uninitialized Func field, or a conditional expression that resolved to null.

Common situations: Storing selector delegates in nullable fields or configuration-driven lookups that fail to resolve; passing the result of a method that returns Func<TSource, Task<T>>? when the mapping is not registered; copy-paste refactors that removed the lambda body.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.StandardSequenceOperators.cs:1253

        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <typeparam name="TTaskResult">The type of the results produced by the projected intermediate tasks.</typeparam>
        /// <typeparam name="TResult">The type of the elements in the result sequence, obtained by using the selector to combine source sequence elements with their corresponding intermediate task results.</typeparam>
        /// <param name="source">An observable sequence of elements to project.</param>
        /// <param name="taskSelector">A transform function to apply to each element.</param>
        /// <param name="resultSelector">A transform function to apply to each element of the intermediate sequence.</param>
        /// <returns>An observable sequence whose elements are the result of obtaining a task for each element of the input sequence and then mapping the task's result and its corresponding source element to a result element.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="taskSelector"/> or <paramref name="resultSelector"/> is null.</exception>
        /// <remarks>This overload supports using LINQ query comprehension syntax in C# and Visual Basic to compose observable sequences and tasks, without requiring manual conversion of the tasks to observable sequences using <see cref="TaskObservableExtensions.ToObservable{TResult}(Task{TResult})"/>.</remarks>
        public static IObservable<TResult> SelectMany<TSource, TTaskResult, TResult>(this IObservable<TSource> source, Func<TSource, Task<TTaskResult>> taskSelector, Func<TSource, TTaskResult, TResult> resultSelector)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

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

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

            return s_impl.SelectMany(source, taskSelector, resultSelector);
        }

        /// <summary>
        /// Projects each element of an observable sequence to a task by incorporating the element's index, invokes the result selector for the source element and the task result, and merges the results into one observable sequence.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <typeparam name="TTaskResult">The type of the results produced by the projected intermediate tasks.</typeparam>
        /// <typeparam name="TResult">The type of the elements in the result sequence, obtained by using the selector to combine source sequence elements with their corresponding intermediate task results.</typeparam>
        /// <param name="source">An observable sequence of elements to project.</param>
        /// <param name="taskSelector">A transform function to apply to each element; the second parameter of the function represents the index of the source element.</param>

View on GitHub (pinned to 94b5d5ab91)