dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'options')

Error message

Value cannot be null. (Parameter 'options')

What it means

Observable.StartAsync(functionAsync, TaskObservationOptions) throws ArgumentNullException when options is null. TaskObservationOptions controls scheduling and exception-after-unsubscribe behavior; without it the library cannot build the TaskObservationOptions.Value it needs, so it fails fast.

Solutions

  1. Pass a constructed TaskObservationOptions instance, or use the IScheduler overload with a concrete scheduler
  2. Use the parameterless-ish alternatives: StartAsync(functionAsync) or StartAsync(functionAsync, scheduler) which build default options internally
  3. Coalesce to a default options object when loaded from configuration

Example fix

// before
var opts = config.Get<TaskObservationOptions>(); // may be null
Observable.StartAsync(workAsync, opts);
// after
var opts = config.Get<TaskObservationOptions>() ?? new TaskObservationOptions(Scheduler.Default);
Observable.StartAsync(workAsync, opts);
Defensive patterns

Strategy: validation

Validate before calling

if (options == null) throw new ArgumentNullException(nameof(options));
Observable.StartAsync(functionAsync, options);

Type guard

bool HasOptions(TaskObservationOptions o) => o is not null;

Try / catch

try { Observable.StartAsync(functionAsync, options); }
catch (ArgumentNullException ex) when (ex.ParamName == "options") { Observable.StartAsync(functionAsync, Scheduler.Default); }

Prevention

When it happens

Trigger: Calling Observable.StartAsync<TResult>(Func<Task<TResult>>, TaskObservationOptions) with options == null, e.g. options built from a nullable configuration object or defaulting code path that never assigned it.

Common situations: Config-driven setups where an options section was absent, factory methods returning null instead of default options, or refactors that removed a default instance.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Async.cs:1101

        /// <param name="options">Controls how the tasks's progress is observed.</param>
        /// <returns>An observable sequence exposing the function's result value, or an exception.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="functionAsync"/> is null.</exception>
        /// <remarks>
        /// <list type="bullet">
        /// <item><description>The function is started immediately, not during the subscription of the resulting sequence.</description></item>
        /// <item><description>Multiple subscriptions to the resulting sequence can observe the function's result.</description></item>
        /// </list>
        /// </remarks>
        public static IObservable<TResult> StartAsync<TResult>(Func<Task<TResult>> functionAsync, TaskObservationOptions options)
        {
            if (functionAsync == null)
            {
                throw new ArgumentNullException(nameof(functionAsync));
            }

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

            return s_impl.StartAsync(functionAsync, options.ToValue());
        }

        /// <summary>
        /// Invokes the asynchronous function, surfacing the result through an observable sequence.
        /// The CancellationToken is shared by all subscriptions on the resulting observable sequence. See the remarks section for more information.
        /// </summary>
        /// <typeparam name="TResult">The type of the result returned by the asynchronous function.</typeparam>
        /// <param name="functionAsync">Asynchronous function to run.</param>
        /// <returns>An observable sequence exposing the function's result value, or an exception.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="functionAsync"/> is null.</exception>
        /// <remarks>
        /// <list type="bullet">
        /// <item><description>The function is started immediately, not during the subscription of the resulting sequence.</description></item>
        /// <item><description>Multiple subscriptions to the resulting sequence can observe the function's result.</description></item>
        /// <item><description>

View on GitHub (pinned to 94b5d5ab91)