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
- Provide a non-null onNext action, even an empty one: x => { }.
- Use a different subscription style (e.g. full AsyncObserver) if handlers are dynamic.
- 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
- Always pass a concrete onNext action; use x => { } for a no-op.
- Do not store handlers in nullable fields consumed later; assign at construction.
- Default missing handler-table entries to a no-op lambda.
- Enable nullable reference types to catch unassigned Action fields.
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
- Value cannot be null. (Parameter 'onCompletedAsync')
- Value cannot be null. (Parameter 'onError')
- source
- ArgumentNullException(nameof(onError))
- ArgumentNullException(nameof(onCompleted))
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)