{"record":{"id":"96dae6ccbecc4acc","repo":"dotnet/reactive","slug":"conversion-observable-events","errorCode":null,"errorMessage":"conversion","messagePattern":"conversion","errorType":"exception","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Linq/Observable.Events.cs","lineNumber":1172,"sourceCode":"        /// The current <see cref=\"SynchronizationContext\"/> is captured during the call to FromEvent, and is used to post add and remove handler invocations.\n        /// This behavior ensures add and remove handler operations for thread-affine events are accessed from the same context, as required by some UI frameworks.\n        /// </para>\n        /// <para>\n        /// If no SynchronizationContext is present at the point of calling FromEvent, add and remove handler invocations are made synchronously on the thread\n        /// making the Subscribe or Dispose call, respectively.\n        /// </para>\n        /// <para>\n        /// It's recommended to lift FromEvent calls outside event stream query expressions due to the free-threaded nature of Reactive Extensions. Doing so\n        /// makes the captured SynchronizationContext predictable. This best practice also reduces clutter of bridging code inside queries, making the query expressions\n        /// more concise and easier to understand.\n        /// </para>\n        /// </remarks>\n        /// <seealso cref=\"ToEvent\"/>\n        public static IObservable<TEventArgs> FromEvent<TDelegate, TEventArgs>(Func<Action<TEventArgs>, TDelegate> conversion, Action<TDelegate> addHandler, Action<TDelegate> removeHandler)\n        {\n            if (conversion == null)\n            {\n                throw new ArgumentNullException(nameof(conversion));\n            }\n\n            if (addHandler == null)\n            {\n                throw new ArgumentNullException(nameof(addHandler));\n            }\n\n            if (removeHandler == null)\n            {\n                throw new ArgumentNullException(nameof(removeHandler));\n            }\n\n            return s_impl.FromEvent(conversion, addHandler, removeHandler);\n        }\n\n        /// <summary>\n        /// 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.\n        /// For conversion of events conforming to the standard .NET event pattern, use any of the FromEventPattern overloads instead.","sourceCodeStart":1154,"sourceCodeEnd":1190,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Linq/Observable.Events.cs#L1154-L1190","documentation":"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.","triggerScenarios":"Calling FromEvent(null, addHandler, removeHandler), often when the converter lambda is stored in a nullable field or returned from a factory that produced null.","commonSituations":"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.","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."],"exampleFix":"// before\nObservable.FromEvent<RoutedEventHandler, RoutedEventArgs>(null, h => el.AddHandler(UIElement.PreviewMouseDownEvent, h), h => el.RemoveHandler(UIElement.PreviewMouseDownEvent, h));\n// after\nObservable.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)));","handlingStrategy":"validation","validationCode":"if (conversion is null) throw new ArgumentNullException(nameof(conversion)); // before calling FromEvent","typeGuard":"bool HasConversion<TDelegate>(Func<Action<EventArgs>, TDelegate> c) => c is not null;","tryCatchPattern":null,"preventionTips":["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"],"tags":["dotnet","reactive-extensions","null-argument","events"],"backgroundTag":"null-argument","analyzedSha":"94b5d5ab912789f5abe9a72138a25bbd716fe59c","analyzedAt":"2026-09-15T02:26:24.759Z","contentChangedAt":"2026-09-15T02:26:24.759Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}