dotnet/reactive · error · ArgumentNullException
removeHandler
Error message
removeHandler
What it means
System.ArgumentNullException with param name 'removeHandler'. The FromEventPattern(addHandler, removeHandler, scheduler) overload validates all delegate arguments up-front and throws when removeHandler is null. The library requires both subscription hooks to attach and detach the underlying CLR event via the FromEvent operator, so a missing remove handler would leave no way to unsubscribe.
Solutions
- Pass a non-null removeHandler delegate, e.g. h => target.Something -= h, mirroring the add handler.
- If the event truly cannot be unsubscribed, use a different FromEventPattern overload or wrap the event manually instead of passing null.
- Check argument order: (addHandler, removeHandler, scheduler) — a swapped or omitted argument leaves removeHandler null.
- Add an explicit null check with a clear message at your call site to fail fast before calling the library.
Example fix
// before
var obs = AsyncObservable.FromEventPattern(
h => target.Something += h,
null,
scheduler);
// after
var obs = AsyncObservable.FromEventPattern(
h => target.Something += h,
h => target.Something -= h,
scheduler); Defensive patterns
Strategy: validation
Validate before calling
if (addHandler == null) throw new ArgumentNullException(nameof(addHandler)); if (removeHandler == null) throw new ArgumentNullException(nameof(removeHandler)); if (scheduler == null) scheduler = AsyncScheduler.Default;
Type guard
static bool IsValidFromEventPatternArgs(Action<EventHandler> add, Action<EventHandler> remove, IAsyncScheduler sched)
=> add != null && remove != null && sched != null; Try / catch
try
{
var obs = AsyncObservable.FromEventPattern(h => t.E += h, h => t.E -= h, scheduler);
}
catch (ArgumentNullException ex)
{
// ex.ParamName tells which argument was null; log and fail fast
} Prevention
- Always pass paired add/remove lambdas for the event.
- Never pass null for scheduler in the explicit overload; use the shorter overload instead.
- Verify argument order (addHandler, removeHandler, scheduler).
- Add unit tests covering subscription and unsubscription of the bridged event.
When it happens
Trigger: Calling AsyncObservable.FromEventPattern(Action<EventHandler> addHandler, Action<EventHandler> removeHandler, IAsyncScheduler scheduler) and passing null for the removeHandler argument, e.g. FromEventPattern(h => target.Something += h, null, scheduler).
Common situations: Developers wire the add handler but skip the remove handler because the target has no symmetric -= API, or a refactored overload call accidentally drops one of the lambdas; also happens when the two lambdas are built dynamically and one comes back null.
Related errors
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/1f8dfceab03d47dc.
Report an issue: GitHub.
Appendix: source
Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/FromEventPattern.cs:23
using System.Globalization;
using System.Reactive.Concurrency;
using System.Reactive.Disposables;
using System.Reflection;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;
namespace System.Reactive.Linq
{
public partial class AsyncObservable
{
public static IAsyncObservable<EventPattern<object>> FromEventPattern(Action<EventHandler> addHandler, Action<EventHandler> removeHandler) => FromEventPattern(addHandler, removeHandler, GetSchedulerForCurrentContext());
public static IAsyncObservable<EventPattern<object>> FromEventPattern(Action<EventHandler> addHandler, Action<EventHandler> removeHandler, IAsyncScheduler scheduler)
{
if (addHandler == null)
throw new ArgumentNullException(nameof(addHandler));
if (removeHandler == null)
throw new ArgumentNullException(nameof(removeHandler));
if (scheduler == null)
throw new ArgumentNullException(nameof(scheduler));
return FromEvent<EventHandler, object, EventArgs>(
action => new EventHandler((o, e) => action(o, e)),
addHandler,
removeHandler,
scheduler).Select(t => new EventPattern<object>(t.arg1, t.arg2));
}
public static IAsyncObservable<EventPattern<TEventArgs>> FromEventPattern<TDelegate, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler) => FromEventPattern<TDelegate, TEventArgs>(addHandler, removeHandler, GetSchedulerForCurrentContext());
public static IAsyncObservable<EventPattern<TEventArgs>> FromEventPattern<TDelegate, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IAsyncScheduler scheduler)
{
if (addHandler == null)
throw new ArgumentNullException(nameof(addHandler));
if (removeHandler == null)
throw new ArgumentNullException(nameof(removeHandler));View on GitHub (pinned to 94b5d5ab91)