dotnet/reactive · error · ArgumentNullException

ArgumentNullException: addHandler

Error message

ArgumentNullException: addHandler

What it means

FromEventPattern<TSender, TResult>(Action<TypedEventHandler<TSender, TResult>> addHandler, Action<...> removeHandler) converts a WinRT event into an observable, and it throws ArgumentNullException("addHandler") when the addHandler delegate is null. Both handler delegates are required: addHandler attaches the TypedEventHandler when the observable is subscribed.

Solutions

  1. Pass real add/remove accessor method groups, e.g. (h => target.Event += h, h => target.Event -= h).
  2. If wiring via reflection, verify GetAddMethod()/Delegate.CreateDelegate results are non-null before calling and throw a clearer error otherwise.
  3. Check spelling/availability of the WinRT event on the actual runtime type; use the correct overload for non-TypedEventHandler events.

Example fix

// before
Action<TypedEventHandler<Button, object>> add = GetAdd("Click"); // null: bad lookup
Observable.FromEventPattern<Button, object>(add, remove);
// after
var add = new Action<TypedEventHandler<Button, object>>(h => button.Click += h);
var remove = new Action<TypedEventHandler<Button, object>>(h => button.Click -= h);
Observable.FromEventPattern<Button, object>(add, remove);
Defensive patterns

Strategy: validation

Validate before calling

if (addHandler == null || removeHandler == null)
    throw new InvalidOperationException("FromEventPattern requires both add and remove handlers");

Type guard

bool ValidHandlers<TH>(Action<TH>? add, Action<TH>? remove) => add is not null && remove is not null;

Try / catch

try { obs = Observable.FromEventPattern<TSender, TResult>(add, remove); }
catch (ArgumentNullException ex) when (ex.ParamName == "addHandler") { log.Error("add accessor missing for event wiring"); }

Prevention

When it happens

Trigger: Calling Observable.FromEventPattern<TSender, TResult>(null, removeHandler) — passing a null add delegate, often from a variable resolved by reflection or string lookup that yielded null.

Common situations: Reflection-based event wiring where the add accessor name is misspelled or the event doesn't exist on the target type; generic wrappers storing add/remove delegates that weren't initialized; ported code where one of the two delegates was dropped.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive.WindowsRuntime/System.Reactive.Linq/WindowsObservable.Events.cs:33

    /// </summary>
    [CLSCompliant(false)]
    public static partial class WindowsObservable
    {
        /// <summary>
        /// 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 () =>
                {

View on GitHub (pinned to 94b5d5ab91)