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
- Pass a matching remove delegate, e.g. h => target.MyEvent -= h (must mirror addHandler).
- If the API truly lacks removal, consider wrapping with a no-op: h => { } only if leaking a subscription is acceptable — normally not.
- Verify argument order: conversion, addHandler, removeHandler.
- 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
- Always mirror addHandler with removeHandler in the same code block
- Treat a null removeHandler as a bug, never a legitimate value
- Test that disposing the subscription detaches the handler
- Generate add/remove pairs via a helper method to keep them in sync
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)