dotnet/reactive · error · ArgumentNullException
ArgumentNullException: conversion
Error message
ArgumentNullException: conversion
What it means
FromEventPattern<TDelegate, TSender, TResult>(Func<TypedEventHandler<TSender, TResult>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler) supports custom event-handler delegate types. It validates in order and throws ArgumentNullException("conversion") when the conversion function is null. The conversion maps the internal TypedEventHandler to the event's native delegate type and is essential for subscribe/unsubscribe.
Solutions
- Supply a real conversion, e.g. h => new SomeEventHandler(h) (or a lambda matching the delegate's signature).
- If the delegate matches TypedEventHandler exactly, use the simpler two-delegate overload and avoid conversion entirely.
- In generic wrappers, validate the conversion argument before forwarding to Rx and throw a caller-specific error.
Example fix
// before
Observable.FromEventPattern<EventHandler, Sender, Result>(
null, // throws
h => src.Ev += h,
h => src.Ev -= h);
// after
Observable.FromEventPattern<EventHandler, Sender, Result>(
h => (Sender s, Result r) => h(s, r) is implicit — supply explicit:
h => new EventHandler((s, r) => h(s, r)),
h => src.Ev += h,
h => src.Ev -= h); Defensive patterns
Strategy: validation
Validate before calling
if (conversion == null)
throw new InvalidOperationException("conversion delegate required for custom event handler types");
if (addHandler == null || removeHandler == null)
throw new InvalidOperationException("add/remove handlers required"); Type guard
bool ValidConversion<TD, TS, TR>(Func<TypedEventHandler<TS, TR>, TD>? c) => c is not null;
Try / catch
try { obs = Observable.FromEventPattern<TDelegate, TSender, TResult>(conv, add, remove); }
catch (ArgumentNullException ex) when (ex.ParamName == "conversion") { log.Error("conversion missing; use simple overload or provide factory for delegate type"); } Prevention
- Use the simpler FromEventPattern overload when the event uses TypedEventHandler directly
- In generic helpers, validate the conversion parameter before forwarding
- Write the conversion as an explicit lambda so its nullability is visible to the compiler and nullable analysis
When it happens
Trigger: Calling the three-generic overload with conversion == null, e.g. FromEventPattern<RoutedEventHandler, S, R>(null, add, remove), or supplying a conversion variable that wasn't initialized.
Common situations: Generic helper methods forwarding a conversion parameter that callers pass as null; attempts to use the simple overload's pattern with a delegate type that requires explicit conversion; ported code from .NET event idioms where conversion was considered optional.
Related errors
- ArgumentNullException: addHandler
- ArgumentNullException: removeHandler
- Value cannot be null. (Parameter 'addHandler')
- Value cannot be null. (Parameter 'removeHandler')
- addHandler
AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15).
Data as JSON: /api/errors/741120ffcddad771.
Report an issue: GitHub.
Appendix: source
Thrown at Rx.NET/Source/src/System.Reactive.WindowsRuntime/System.Reactive.Linq/WindowsObservable.Events.cs:73
}
/// <summary>
/// Converts a typed event, conforming to the standard event pattern, to an observable sequence.
/// </summary>
/// <typeparam name="TDelegate">The delegate type of the event to be converted.</typeparam>
/// <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="conversion">A function used to convert the given event handler to a delegate compatible with the underlying typed event. The resulting delegate is used in calls to the addHandler and removeHandler action parameters.</param>
/// <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="conversion"/> or <paramref name="addHandler"/> or <paramref name="removeHandler"/> is null.</exception>
/// <seealso cref="ToEventPattern"/>
public static IObservable<EventPattern<TSender, TResult>> FromEventPattern<TDelegate, TSender, TResult>(Func<TypedEventHandler<TSender, TResult>, 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 Observable.Create<EventPattern<TSender, TResult>>(observer =>
{
var h = conversion(new TypedEventHandler<TSender, TResult>((sender, args) =>
{
observer.OnNext(new EventPattern<TSender, TResult>(sender, args));
}));View on GitHub (pinned to 94b5d5ab91)