dotnet/reactive · error · ArgumentNullException
Value cannot be null. (Parameter 'addHandler')
Error message
Value cannot be null. (Parameter 'addHandler')
What it means
Observable.FromEventPattern(addHandler, removeHandler) needs delegates that attach and detach the .NET event handler. A null addHandler is rejected immediately with ArgumentNullException because subscription could not wire up the event.
Solutions
- Pass non-null lambdas such as h => target.Event += h and h => target.Event -= h
- Check that reflection-obtained add accessor MethodInfo is non-null and create the delegate successfully
- Fix the wrapper/helper that forwarded a null addHandler
Example fix
// before var add = (Action<EventHandler>)null; Observable.FromEventPattern(add, h => target.Click -= h); // after Observable.FromEventPattern(h => target.Click += h, h => target.Click -= h);
Defensive patterns
Strategy: validation
Validate before calling
if (addHandler == null) throw new ArgumentNullException(nameof(addHandler)); if (removeHandler == null) throw new ArgumentNullException(nameof(removeHandler)); var seq = Observable.FromEventPattern(addHandler, removeHandler);
Type guard
static bool HasEventHandlers(Action<EventHandler> add, Action<EventHandler> remove) => add is not null && remove is not null;
Try / catch
try
{
var seq = Observable.FromEventPattern(addHandler, removeHandler);
}
catch (ArgumentNullException ex) when (ex.ParamName == "addHandler")
{
throw new InvalidOperationException("Event add accessor was not resolved", ex);
} Prevention
- Inline the h => target.Event += h / h => target.Event -= h lambdas at the call site
- When using reflection, assert EventInfo.GetAddMethod() and GetRemoveMethod() are non-null
- Prefer typed FromEventPattern overloads (target, nameof(event)) to avoid manual delegates
When it happens
Trigger: Calling FromEventPattern(Action<EventHandler>, Action<EventHandler>) with a null first argument, e.g. forwarding reflection-resolved add accessors that were not found.
Common situations: EventInfo.GetAddMethod() returning null and being passed along; storing the delegates in fields that were never initialized; generic event-wrapping helpers forwarding caller-supplied nulls.
Related errors
- Value cannot be null. (Parameter 'removeHandler')
- observer
- scheduler (Value cannot be null)
- action (Value cannot be null)
- scheduler (Value cannot be null)
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/b9e760ee9e2e6b5d.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Events.cs:52
/// The current <see cref="SynchronizationContext"/> is captured during the call to FromEventPattern, and is used to post add and remove handler invocations.
/// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.
/// </para>
/// <para>
/// If no SynchronizationContext is present at the point of calling FromEventPattern, add and remove handler invocations are made synchronously on the thread
/// making the Subscribe or Dispose call, respectively.
/// </para>
/// <para>
/// It's recommended to lift FromEventPattern calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so
/// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions
/// more concise and easier to understand.
/// </para>
/// </remarks>
/// <seealso cref="ToEventPattern"/>
public static IObservable<EventPattern<object>> FromEventPattern(Action<EventHandler> addHandler, Action<EventHandler> removeHandler)
{
if (addHandler == null)
{
throw new ArgumentNullException(nameof(addHandler));
}
if (removeHandler == null)
{
throw new ArgumentNullException(nameof(removeHandler));
}
return s_impl.FromEventPattern(addHandler, removeHandler);
}
/// <summary>
/// Converts a .NET event, conforming to the standard .NET event pattern based on <see cref="EventHandler"/>, to an observable sequence.
/// Each event invocation is surfaced through an OnNext message in the resulting sequence.
/// For conversion of events that don't conform to the standard .NET event pattern, use any of the FromEvent overloads instead.
/// </summary>
/// <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>View on GitHub (pinned to 94b5d5ab91)