dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'onNext')

Error message

Value cannot be null. (Parameter 'onNext')

What it means

The blocking ForEach invokes onNext for every element while blocking the caller until termination; a null action is rejected up front because every notification would fail. Fix: pass a non-null Action<TSource>, or use Subscribe if you need non-blocking behavior.

Solutions

  1. Pass a non-null action or use Subscribe with default (empty) behavior instead
  2. Default the action to _ => { } when optional
  3. Guard onNext != null before calling ForEach

Example fix

// before
source.ForEach(maybeAction);
// after
source.ForEach(maybeAction ?? (_ => { }));
Defensive patterns

Strategy: validation

Validate before calling

if (onNext == null) throw new InvalidOperationException("onNext callback is required");

Type guard

bool HasAction<T>(Action<T>? a) => a is not null;

Try / catch

try { source.ForEach(onNext); } catch (ArgumentNullException ex) when (ex.ParamName == "onNext") { /* supply a default action */ }

Prevention

When it happens

Trigger: Calling Observable.ForEach<T>(source, null), e.g. a callback field that was never assigned.

Common situations: Optional callback parameters passed straight through; event-handler delegates resolved from DI or configuration that ended up null.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Blocking.cs:223

        /// <summary>
        /// Invokes an action for each element in the observable sequence, and blocks until the sequence is terminated.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <param name="source">Source sequence.</param>
        /// <param name="onNext">Action to invoke for each element in the observable sequence.</param>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="onNext"/> is null.</exception>
        /// <remarks>Because of its blocking nature, this operator is mainly used for testing.</remarks>
        [Obsolete(Constants_Linq.UseAsync)]
        public static void ForEach<TSource>(this IObservable<TSource> source, Action<TSource> onNext)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

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

            s_impl.ForEach(source, onNext);
        }

        /// <summary>
        /// Invokes an action for each element in the observable sequence, incorporating the element's index, and blocks until the sequence is terminated.
        /// </summary>
        /// <typeparam name="TSource">The type of the elements in the source sequence.</typeparam>
        /// <param name="source">Source sequence.</param>
        /// <param name="onNext">Action to invoke for each element in the observable sequence.</param>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="onNext"/> is null.</exception>
        /// <remarks>Because of its blocking nature, this operator is mainly used for testing.</remarks>
        [Obsolete(Constants_Linq.UseAsync)]
        public static void ForEach<TSource>(this IObservable<TSource> source, Action<TSource, int> onNext)
        {
            if (source == null)
            {

View on GitHub (pinned to 94b5d5ab91)