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
- Pass a real converter, e.g. h => (EventHandler<MyArgs>)h.Invoke or h => new RoutedEventHandler(h).
- Check any factory/lookup producing the converter for null-returning paths.
- Prefer the simpler FromEvent overloads that don't need a converter when using standard EventHandler<TEventArgs>.
- 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
- Use static converter lambdas so they can never be null
- Prefer EventHandler<TEventArgs> overloads that need no converter
- Never store converters in nullable fields initialized later
- Check parameter order (conversion, add, remove) in copy-pasted calls
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)