dotnet/reactive · error · ArgumentNullException

ArgumentNull_Generic

Error message

ArgumentNull_Generic

What it means

FromEvent converts .NET-style add/removeHandler event subscriptions into an async observable. This overload accepts a conversion delegate (Action<T1,T2> -> TDelegate), add/remove handlers, and a scheduler; it throws ArgumentNullException (rendered here as the generic ArgumentNull_Generic message) when any of `conversion`, `addHandler`, `removeHandler`, or `scheduler` is null.

Solutions

  1. Supply all four arguments: a conversion such as `h => ( EventHandler)h.Invoke` (matching TDelegate), non-null addHandler/removeHandler, and a scheduler like AsyncScheduler.Immediate (or the library's default).
  2. Use the simpler overload FromEvent(addHandler, removeHandler) that internally supplies `h => h` conversion and GetSchedulerForCurrentContext() when you don't need custom conversion/scheduling.
  3. Check the source of the scheduler argument; if it comes from configuration or a factory, assert non-null before calling.

Example fix

// before
AsyncObservable.FromEvent<Action<int,string>, int, string>(null, addHandler, removeHandler, null);
// after
var conv = new Func<Action<int,string>, Action<int,string>>(h => h); // or real delegate conversion
AsyncObservable.FromEvent<Action<int,string>, int, string>(conv, addHandler, removeHandler, AsyncScheduler.Immediate);
Defensive patterns

Strategy: validation

Validate before calling

if (conversion is null || addHandler is null || removeHandler is null || scheduler is null)
    throw new ArgumentNullException(conversion is null ? nameof(conversion)
        : addHandler is null ? nameof(addHandler)
        : removeHandler is null ? nameof(removeHandler) : nameof(scheduler));

Type guard

bool CanWire<TDel>(Func<Action<TDel>,TDel> conversion, Action<TDel> add, Action<TDel> remove, IAsyncScheduler sched)
    => conversion is not null && add is not null && remove is not null && sched is not null;

Try / catch

try
{
    var obs = AsyncObservable.FromEvent<TDelegate,int,string>(conversion, addHandler, removeHandler, scheduler);
}
catch (ArgumentNullException ex)
{
    // ex.ParamName tells which of conversion/addHandler/removeHandler/scheduler was null
}

Prevention

When it happens

Trigger: Calling the FromEvent<T1,T2> overload that takes (conversion, addHandler, removeHandler, scheduler) with any of those four arguments null - e.g. a scheduler obtained from a resolver that returned null, or an event handler registry with no add/remove hooks registered.

Common situations: Passing a scheduler from DI or a context provider that returned null; wiring events dynamically where the add/remove accessor delegates are null; forgetting the conversion lambda in the overload that requires it.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/FromEvent.Generated.cs:56

                throw new ArgumentNullException(nameof(conversion));
            if (addHandler == null)
                throw new ArgumentNullException(nameof(addHandler));
            if (removeHandler == null)
                throw new ArgumentNullException(nameof(removeHandler));

            return FromEventCore<TDelegate, T1, T2>(conversion, addHandler, removeHandler, GetSchedulerForCurrentContext());
        }

        public static IAsyncObservable<(T1 arg1, T2 arg2)> FromEvent<TDelegate, T1, T2>(Func<Action<T1, T2>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IAsyncScheduler scheduler)
        {
            if (conversion == null)
                throw new ArgumentNullException(nameof(conversion));
            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 FromEventCore<TDelegate, T1, T2>(conversion, addHandler, removeHandler, scheduler);
        }

        private static IAsyncObservable<(T1 arg1, T2 arg2)> FromEventCore<TDelegate, T1, T2>(Func<Action<T1, T2>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IAsyncScheduler scheduler)
        {
            return
                SynchronizeEvents(
                    Create<(T1 arg1, T2 arg2)>(observer =>
                    {
                        var handler = new Action<T1, T2>((arg1, arg2) =>
                        {
                            observer.OnNextAsync((arg1, arg2)); // REVIEW: Fire-and-forget can lead to out of order processing, and observers may reject these calls as "busy".
                        });

                        var converted = conversion(handler);

                        addHandler(converted);

View on GitHub (pinned to 94b5d5ab91)