dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'onCompletedAsync')

Error message

Value cannot be null. (Parameter 'onCompletedAsync')

What it means

This ArgumentNullException is thrown by the SubscribeAsync extension method when the onCompletedAsync delegate is null. AsyncRx.NET validates all observer callbacks up front so that a null handler fails fast at subscription time instead of throwing a NullReferenceException when the source completes. Every parameter of SubscribeAsync is required because the underlying AsyncObserver needs all three handlers.

Solutions

  1. Pass a no-op completion handler: () => new ValueTask() or () => default.
  2. Use a simpler overload such as SubscribeAsync(source, onNextAsync) which supplies default error/completion handlers for you.
  3. Audit the call site for a variable that is null before being passed as onCompletedAsync.

Example fix

// before
await source.SubscribeAsync(async x => await Handle(x), null);
// after
await source.SubscribeAsync(async x => await Handle(x), () => default);
Defensive patterns

Strategy: validation

Validate before calling

if (onCompletedAsync == null) throw new ArgumentNullException(nameof(onCompletedAsync));
await source.SubscribeAsync(onNextAsync, onCompletedAsync);

Type guard

bool IsValidHandlers(Func<T, ValueTask> next, Func<ValueTask> completed) => next != null && completed != null;

Try / catch

try
{
    await source.SubscribeAsync(onNextAsync, onCompletedAsync);
}
catch (ArgumentNullException ex) when (ex.ParamName == "onCompletedAsync")
{
    await source.SubscribeAsync(onNextAsync, () => default);
}

Prevention

When it happens

Trigger: Calling source.SubscribeAsync(source, onNextAsync, onCompletedAsync) (the two-delegate overload) with null as the completion callback, e.g. SubscribeAsync(src, async x => await Handle(x), null).

Common situations: Developers porting from Rx.NET where OnCompleted can be omitted, or building handlers conditionally where the completion lambda ends up null after an if-statement, or passing optional Func<ValueTask> parameters straight through.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/AsyncObservableExtensions.cs:41

        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (onNextAsync == null)
                throw new ArgumentNullException(nameof(onNextAsync));
            if (onErrorAsync == null)
                throw new ArgumentNullException(nameof(onErrorAsync));

            return source.SubscribeAsync(new AsyncObserver<T>(onNextAsync, onErrorAsync, () => default));
        }

        public static ValueTask<IAsyncDisposable> SubscribeAsync<T>(this IAsyncObservable<T> source, Func<T, ValueTask> onNextAsync, Func<ValueTask> onCompletedAsync)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (onNextAsync == null)
                throw new ArgumentNullException(nameof(onNextAsync));
            if (onCompletedAsync == null)
                throw new ArgumentNullException(nameof(onCompletedAsync));

            return source.SubscribeAsync(new AsyncObserver<T>(onNextAsync, ex => new ValueTask(Task.FromException(ex)), onCompletedAsync));
        }

        public static ValueTask<IAsyncDisposable> SubscribeAsync<T>(this IAsyncObservable<T> source, Func<T, ValueTask> onNextAsync, Func<Exception, ValueTask> onErrorAsync, Func<ValueTask> onCompletedAsync)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (onNextAsync == null)
                throw new ArgumentNullException(nameof(onNextAsync));
            if (onErrorAsync == null)
                throw new ArgumentNullException(nameof(onErrorAsync));
            if (onCompletedAsync == null)
                throw new ArgumentNullException(nameof(onCompletedAsync));

            return source.SubscribeAsync(new AsyncObserver<T>(onNextAsync, onErrorAsync, onCompletedAsync));
        }

View on GitHub (pinned to 94b5d5ab91)