dotnet/reactive · error · ArgumentNullException

removeHandler

Error message

removeHandler

What it means

Observable.FromEvent<TDelegate,TEventArgs> throws ArgumentNullException when removeHandler is null. removeHandler unsubscribes the handler when the observable subscription is disposed; Rx validates it up-front to guarantee proper cleanup.

Solutions

  1. Pass a matching remove delegate, e.g. h => target.MyEvent -= h (must mirror addHandler).
  2. If the API truly lacks removal, consider wrapping with a no-op: h => { } only if leaking a subscription is acceptable — normally not.
  3. Verify argument order: conversion, addHandler, removeHandler.
  4. Unit-test dispose of the subscription to confirm the remove handler is supplied and correct.

Example fix

// before
Observable.FromEvent<EventHandler, EventArgs>(conv, h => target.MyEvent += h, null);
// after
Observable.FromEvent<EventHandler, EventArgs>(conv, h => target.MyEvent += h, h => target.MyEvent -= h);
Defensive patterns

Strategy: validation

Validate before calling

if (removeHandler is null) throw new ArgumentNullException(nameof(removeHandler));

Type guard

bool HasRemove<TDelegate>(Action<TDelegate> remove) => remove is not null;

Prevention

When it happens

Trigger: Calling FromEvent(conversion, addHandler, null), e.g. when only subscription logic was written and unsubscribe was left as null or forgotten.

Common situations: Generating add/remove pairs with T4/templates where one side failed; passing the same lambda for both but one argument left null; incomplete adapter code around legacy events.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Events.cs:1182

        /// more concise and easier to understand.
        /// </para>
        /// </remarks>
        /// <seealso cref="ToEvent"/>
        public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(Func<Action<TEventArgs>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler)
        {
            if (conversion == null)
            {
                throw new ArgumentNullException(nameof(conversion));
            }

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

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

            return s_impl.FromEvent(conversion, addHandler, removeHandler);
        }

        /// <summary>
        /// Converts a .NET event to an observable sequence, using a conversion function to obtain the event delegate. Each event invocation is surfaced through an OnNext message in the resulting sequence.
        /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.
        /// </summary>
        /// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
        /// <typeparam name="TEventArgs">The type of the event data generated by the event.</typeparam>
        /// <param name="conversion">A function used to convert the given event handler to a delegate compatible with the underlying .NET event. The resulting delegate is used in calls to the addHandler and removeHandler action parameters.</param>
        /// <param name="addHandler">Action that attaches the given event handler to the underlying .NET event.</param>
        /// <param name="removeHandler">Action that detaches the given event handler from the underlying .NET event.</param>
        /// <param name="scheduler">The scheduler to run the add and remove event handler logic on.</param>
        /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="conversion"/> or <paramref name="addHandler"/> or <paramref name="removeHandler"/> or <paramref name="scheduler"/> is null.</exception>
        /// <remarks>

View on GitHub (pinned to 94b5d5ab91)