dotnet/reactive · error · ArgumentNullException

conversion

Error message

conversion

What it means

Observable.FromEvent<TDelegate,TEventArgs>(Func<Action<TEventArgs>,TDelegate> conversion, ...) throws ArgumentNullException when the conversion delegate is null. The conversion function adapts Action<TEventArgs> handlers into the custom delegate type, so it is mandatory.

Solutions

  1. Pass a real converter, e.g. h => (EventHandler<MyArgs>)h.Invoke or h => new RoutedEventHandler(h).
  2. Check any factory/lookup producing the converter for null-returning paths.
  3. Prefer the simpler FromEvent overloads that don't need a converter when using standard EventHandler<TEventArgs>.
  4. Validate arguments before the call if they come from dynamic sources.

Example fix

// before
Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(null, h => el.AddHandler(UIElement.PreviewMouseDownEvent, h), h => el.RemoveHandler(UIElement.PreviewMouseDownEvent, h));
// after
Observable.FromEvent<RoutedEventHandler, RoutedEventArgs>(h => (Action<RoutedEventArgs>)(e => h(el, e)) is { } _ ? h2 => el.AddHandler(UIElement.PreviewMouseDownEvent, new RoutedEventHandler(h2)) : h2 => el.AddHandler(UIElement.PreviewMouseDownEvent, new RoutedEventHandler(h2)), h => el.RemoveHandler(UIElement.PreviewMouseDownEvent, new RoutedEventHandler(h)));
Defensive patterns

Strategy: validation

Validate before calling

if (conversion is null) throw new ArgumentNullException(nameof(conversion)); // before calling FromEvent

Type guard

bool HasConversion<TDelegate>(Func<Action<EventArgs>, TDelegate> c) => c is not null;

Prevention

When it happens

Trigger: Calling FromEvent(null, addHandler, removeHandler), often when the converter lambda is stored in a nullable field or returned from a factory that produced null.

Common situations: Handing in a MethodInfo/Delegate cast that ended up null; conditional converter selection defaulting to null; copy-paste moving the converter out but not passing it.

Related errors


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

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Linq/Observable.Events.cs:1172

        /// The current <see cref="SynchronizationContext"/> is captured during the call to FromEvent, 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 FromEvent, 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 FromEvent 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="ToEvent"/>
        public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(Func<Action<TEventArgs>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler)
        {
            if (conversion == null)
            {
                throw new ArgumentNullException(nameof(conversion));
            }

            if (addHandler == null)
            {
                throw new ArgumentNullException(nameof(addHandler));
            }

            if (removeHandler == null)
            {
                throw new ArgumentNullException(nameof(removeHandler));
            }

            return s_impl.FromEvent(conversion, addHandler, removeHandler);
        }

        /// <summary>
        /// Converts a .NET event to an observable sequence, using a conversion function to obtain the event delegate. Each event invocation is surfaced through an OnNext message in the resulting sequence.
        /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.

View on GitHub (pinned to 94b5d5ab91)