dotnet/reactive · error · ArgumentNullException

Value cannot be null. (Parameter 'invokeHandler')

Error message

Value cannot be null. (Parameter 'invokeHandler')

What it means

The EventPatternSourceBaseInternal constructor requires a non-null invokeHandler delegate, used to raise the event for each element of the sequence. A null delegate is rejected with ArgumentNullException at construction, matching the constructor's documented exception.

Solutions

  1. Pass a valid Action<Action<TSender,TEventArgs>, EventPattern<TSender,TEventArgs>> delegate.
  2. Initialize the handler delegate before calling the base constructor.
  3. Throw a descriptive error or provide a no-op delegate if invocation is legitimately absent.

Example fix

// before
protected MySource(Func<Action<Handler> > lazy)
    : base(source, lazy?.Handler) { } // null until initialized
// after
protected MySource(IAsyncObservable<EventPattern<object, EventArgs>> source)
    : base(source, (h, e) => Handler(h, e)) { }
Defensive patterns

Strategy: validation

Validate before calling

if (invokeHandler is null) throw new ArgumentNullException(nameof(invokeHandler));
// then call the base constructor with invokeHandler

Type guard

static bool HasInvokeHandler(Action<Action<TSender,TEventArgs>, EventPattern<TSender,TEventArgs>>? h) => h is not null;

Try / catch

try { CreateEventPatternSource(source, invokeHandler); }
catch (ArgumentNullException ex) when (ex.ParamName == "invokeHandler")
{ log.LogError("invokeHandler delegate was null"); }

Prevention

When it happens

Trigger: Deriving from EventPatternSourceBaseInternal and passing null (or an uninitialized method-group/field delegate) as the invokeHandler constructor argument.

Common situations: Passing a delegate field assigned only later in initialization (null at base-constructor time); refactor renamed the target method so the method group no longer resolves and someone replaced it with null; conditional delegate selection yielding null.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Internal/EventPatternSourceBaseInternal.cs:37

    /// avoid a conflict. Work out whether we could in fact just use the type defined in System.Reactive. It's not identical,
    /// but perhaps it offers what we need.
    /// </remarks>
    internal abstract class EventPatternSourceBaseInternal<TSender, TEventArgs>
    {
        private readonly IAsyncObservable<EventPattern<TSender, TEventArgs>> _source;
        private readonly Dictionary<Delegate, Stack<IAsyncDisposable>> _subscriptions;
        private readonly Action<Action<TSender, TEventArgs>, /*object,*/ EventPattern<TSender, TEventArgs>> _invokeHandler;

        /// <summary>
        /// Creates a new event pattern source.
        /// </summary>
        /// <param name="source">Source sequence to expose as an event.</param>
        /// <param name="invokeHandler">Delegate used to invoke the event for each element of the sequence.</param>
        /// <exception cref="ArgumentNullException"><paramref name="source"/> or <paramref name="invokeHandler"/> is null.</exception>
        protected EventPatternSourceBaseInternal(IAsyncObservable<EventPattern<TSender, TEventArgs>> source, Action<Action<TSender, TEventArgs>, /*object,*/ EventPattern<TSender, TEventArgs>> invokeHandler)
        {
            _source = source ?? throw new ArgumentNullException(nameof(source));
            _invokeHandler = invokeHandler ?? throw new ArgumentNullException(nameof(invokeHandler));
            _subscriptions = new Dictionary<Delegate, Stack<IAsyncDisposable>>();
        }

        /// <summary>
        /// Adds the specified event handler, causing a subscription to the underlying source.
        /// </summary>
        /// <param name="handler">Event handler to add. The same delegate should be passed to the Remove operation in order to remove the event handler.</param>
        /// <param name="invoke">Invocation delegate to raise the event in the derived class.</param>
        /// <exception cref="ArgumentNullException"><paramref name="handler"/> or <paramref name="invoke"/> is null.</exception>
        protected void Add(Delegate handler, Action<TSender, TEventArgs> invoke)
        {
            if (handler == null)
                throw new ArgumentNullException(nameof(handler));
            if (invoke == null)
                throw new ArgumentNullException(nameof(invoke));

            var gate = new object();
            var isAdded = false;

View on GitHub (pinned to 94b5d5ab91)