dotnet/reactive · error · ArgumentNullException
ArgumentNullException: removeHandler
Error message
ArgumentNullException: removeHandler
What it means
In FromEventPattern<TSender, TResult>(addHandler, removeHandler), after validating addHandler the method validates removeHandler and throws ArgumentNullException("removeHandler") when it is null. Both delegates are required so the observable can attach on Subscribe and detach on Dispose.
Solutions
- Provide the real remove accessor: h => target.Event -= h.
- If reflection fails to find the remove method, surface a descriptive error rather than passing null.
- Never substitute null for unsubscribe; a no-op remove delegate (h => { }) at least avoids the throw, but prefer correct detachment to avoid leaks.
Example fix
// before
Observable.FromEventPattern<Button, object>(
h => button.Click += h,
null); // throws
// after
Observable.FromEventPattern<Button, object>(
h => button.Click += h,
h => button.Click -= h); Defensive patterns
Strategy: validation
Validate before calling
if (removeHandler == null)
throw new InvalidOperationException("removeHandler required to detach on dispose"); Type guard
bool HasRemove<TH>(Action<TH>? remove) => remove is not null;
Try / catch
try { obs = Observable.FromEventPattern<TSender, TResult>(add, remove); }
catch (ArgumentNullException ex) when (ex.ParamName == "removeHandler") { log.Error("remove accessor missing; subscription would leak"); } Prevention
- Always pair add and remove: h => e += h with h => e -= h
- If reflection can't find a remove accessor, treat it as a wiring bug, not an optional value
- Review wrappers around legacy events to guarantee unsubscribe support
When it happens
Trigger: Calling Observable.FromEventPattern<TSender, TResult>(addHandler, null) — a null remove delegate, commonly from reflection lookups that failed to find the remove accessor, or building add/remove pairs where only add was supplied.
Common situations: Reflection-based wiring missing the remove accessor (GetRemoveMethod returned null); event objects exposed through wrappers where unsubscribe isn't implemented and null was passed instead; copy-paste errors omitting the second delegate.
Related errors
- ArgumentNullException: addHandler
- ArgumentNullException: conversion
- Value cannot be null. (Parameter 'addHandler')
- Value cannot be null. (Parameter 'removeHandler')
- addHandler
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/d78941d675ee20eb.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive.WindowsRuntime/System.Reactive.Linq/WindowsObservable.Events.cs:38
/// Converts a typed event, conforming to the standard event pattern, to an observable sequence.
/// </summary>
/// <typeparam name="TSender">The type of the sender that raises the event.</typeparam>
/// <typeparam name="TResult">The type of the event data generated by the event.</typeparam>
/// <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>
/// <returns>The observable sequence that contains data representations of invocations of the underlying typed event.</returns>
/// <exception cref="ArgumentNullException"><paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
/// <seealso cref="ToEventPattern"/>
public static IObservable<EventPattern<TSender, TResult>> FromEventPattern<TSender, TResult>(Action<TypedEventHandler<TSender, TResult>> addHandler, Action<TypedEventHandler<TSender, TResult>> removeHandler)
{
if (addHandler == null)
{
throw new ArgumentNullException(nameof(addHandler));
}
if (removeHandler == null)
{
throw new ArgumentNullException(nameof(removeHandler));
}
return Observable.Create<EventPattern<TSender, TResult>>(observer =>
{
var h = new TypedEventHandler<TSender, TResult>((sender, args) =>
{
observer.OnNext(new EventPattern<TSender, TResult>(sender, args));
});
addHandler(h);
return () =>
{
removeHandler(h);
};
});
}
View on GitHub (pinned to 94b5d5ab91)