dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'onError')

Error message

Value cannot be null. (Parameter 'onError')

What it means

Thrown by the notification-based SelectMany(source, onNext, onError, onCompleted) overload when the onError delegate (Func<Exception, IObservable<TResult>>) is null. Unlike Subscribe, SelectMany cannot accept a null error handler because errors must be translated into a result sequence; validation throws immediately.

Solutions

  1. Pass an error mapping lambda, e.g. ex => Observable.Throw<TResult>(ex), to propagate errors unchanged.
  2. Pass ex => Observable.Empty<TResult>() to swallow errors into an empty sequence (use with care).
  3. Use a logging handler that returns Observable.Throw after recording the exception.

Example fix

// before
var q = source.SelectMany(x => Load(x), null, () => Observable.Empty<Item>());

// after
var q = source.SelectMany(x => Load(x), ex => Observable.Throw<Item>(ex), () => Observable.Empty<Item>());
Defensive patterns

Strategy: validation

Validate before calling

if (onError is null) onError = static ex => Observable.Throw<TResult>(ex); // or throw before the call

Type guard

static bool ErrorMapperPresent<TResult>(Func<Exception, IObservable<TResult>> onError)
    => onError is not null;

Try / catch

try
{
    var q = source.SelectMany(onNext, onError, onCompleted);
}
catch (ArgumentNullException ex) when (ex.ParamName == "onError")
{
    // default: propagate errors unchanged
    q = source.SelectMany(onNext, static ex => Observable.Throw<TResult>(ex), onCompleted);
}

Prevention

When it happens

Trigger: Calling Observable.SelectMany(source, onNext, null, onCompleted) — the error-to-observable mapping is null, e.g. the developer assumed error handling is optional as it is with Subscribe.

Common situations: Migrating code from Subscribe to SelectMany while keeping the Subscribe null-handler convention; scaffolding where only the happy path was written and error handling stubbed as null.

Related errors


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

Appendix: source

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

        /// <param name="onError">A transform function to apply when an error occurs in the source sequence.</param>
        /// <param name="onCompleted">A transform function to apply when the end of the source sequence is reached.</param>
        /// <returns>An observable sequence whose elements are the result of invoking the one-to-many transform function corresponding to each notification in the input sequence.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="onNext"/> or <paramref name="onError"/> or <paramref name="onCompleted"/> is null.</exception>
        public static IObservable<TResult> SelectMany<TSource, TResult>(this IObservable<TSource> source, Func<TSource, IObservable<TResult>> onNext, Func<Exception, IObservable<TResult>> onError, Func<IObservable<TResult>> onCompleted)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

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

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

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

            return s_impl.SelectMany(source, onNext, onError, onCompleted);
        }

        /// <summary>
        /// Projects each notification of an observable sequence to an observable sequence by incorporating the element's index and merges the resulting observable sequences into one observable sequence.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <typeparam name="TResult">The type of the elements in the projected inner sequences and the elements in the merged result sequence.</typeparam>
        /// <param name="source">An observable sequence of notifications to project.</param>
        /// <param name="onNext">A transform function to apply to each element; the second parameter of the function represents the index of the source element.</param>
        /// <param name="onError">A transform function to apply when an error occurs in the source sequence.</param>

View on GitHub (pinned to 94b5d5ab91)