dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'onNext')

Error message

Value cannot be null. (Parameter 'onNext')

What it means

Thrown by the action-based SubscribeAsync(source, onNext) overload when the onNext action is null. onNext is the mandatory element handler; the library fails fast with ArgumentNullException rather than throwing when the first element arrives.

Solutions

  1. Provide a non-null onNext action, even an empty one: x => { }.
  2. Use a different subscription style (e.g. full AsyncObserver) if handlers are dynamic.
  3. Assert/validate handler fields before subscribing.

Example fix

// before
Action<int> handler = _handlers.GetValueOrDefault("onNext"); // may be null
await source.SubscribeAsync(handler);
// after
await source.SubscribeAsync(_handlers.TryGetValue("onNext", out var h) ? h : x => { });
Defensive patterns

Strategy: validation

Validate before calling

if (onNext == null) throw new ArgumentNullException(nameof(onNext));
await source.SubscribeAsync(onNext);

Type guard

bool HasNextHandler<T>(Action<T> onNext) => onNext is not null;

Try / catch

try
{
    await source.SubscribeAsync(onNext);
}
catch (ArgumentNullException ex) when (ex.ParamName == "onNext")
{
    logger.LogError("onNext handler was null");
}

Prevention

When it happens

Trigger: Calling SubscribeAsync(source, (Action<T>)null) or passing a conditionally assigned Action<T> that is null.

Common situations: Event-handler fields not yet assigned, or lambda variables cleared during teardown that are still used for a re-subscription.

Related errors


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

Appendix: source

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

        {
            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));
        }

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

            return source.SubscribeAsync(new AsyncObserver<T>(x => { onNext(x); return default; }, ex => new ValueTask(Task.FromException(ex)), () => default));
        }

        public static ValueTask<IAsyncDisposable> SubscribeAsync<T>(this IAsyncObservable<T> source, Action<T> onNext, Action<Exception> onError)
        {
            if (source == null)
                throw new ArgumentNullException(nameof(source));
            if (onNext == null)
                throw new ArgumentNullException(nameof(onNext));
            if (onError == null)
                throw new ArgumentNullException(nameof(onError));

            return source.SubscribeAsync(new AsyncObserver<T>(x => { onNext(x); return default; }, ex => { onError(ex); return default; }, () => default));
        }

        public static ValueTask<IAsyncDisposable> SubscribeAsync<T>(this IAsyncObservable<T> source, Action<T> onNext, Action onCompleted)
        {

View on GitHub (pinned to 94b5d5ab91)