{"record":{"id":"f9689e65dc181f78","repo":"dotnet/reactive","slug":"addhandler-observable-events","errorCode":null,"errorMessage":"addHandler","messagePattern":"addHandler","errorType":"exception","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Linq/Observable.Events.cs","lineNumber":1177,"sourceCode":"        /// 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.\n        /// </summary>\n        /// <typeparam name=\"TDelegate\">The delegate type of the event to be converted.</typeparam>\n        /// <typeparam name=\"TEventArgs\">The type of the event data generated by the event.</typeparam>\n        /// <param name=\"conversion\">A function used to convert the given event handler to a delegate compatible with the underlying .NET event. The resulting delegate is used in calls to the addHandler and removeHandler action parameters.</param>\n        /// <param name=\"addHandler\">Action that attaches the given event handler to the underlying .NET event.</param>","sourceCodeStart":1159,"sourceCodeEnd":1195,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Linq/Observable.Events.cs#L1159-L1195","documentation":"Observable.FromEvent<TDelegate,TEventArgs> throws ArgumentNullException when addHandler is null. addHandler is the delegate that subscribes your constructed handler to the underlying .NET event, so it must be non-null.","triggerScenarios":"Calling FromEvent(conversion, null, removeHandler), typically when add/remove delegates are built dynamically or one of them was omitted during refactoring.","commonSituations":"Conditional subscription setup where only removeHandler was assigned; passing method group of an object that itself was null and '??' defaulted it away incorrectly; typo passing the same variable twice then nulling one.","solutions":["Pass a valid add delegate, e.g. h => target.MyEvent += h.","Ensure the target object owning the event is non-null before building the handler lambdas.","Check that you didn't pass null for the second parameter when using named methods.","If events come from optional objects, guard the whole subscription in an if (target != null) block."],"exampleFix":"// before\nObservable.FromEvent<EventHandler, EventArgs>(conv, null, h => target.MyEvent -= h);\n// after\nObservable.FromEvent<EventHandler, EventArgs>(conv, h => target.MyEvent += h, h => target.MyEvent -= h);","handlingStrategy":"validation","validationCode":"if (addHandler is null) throw new ArgumentNullException(nameof(addHandler)); if (target is null) throw new InvalidOperationException(\"event source not initialized\");","typeGuard":"bool CanSubscribe<TDelegate>(Action<TDelegate> add) => add is not null;","tryCatchPattern":null,"preventionTips":["Write add/remove pairs together: h => t.E += h, h => t.E -= h","Ensure the event source object exists before creating the observable","Avoid forwarding nullable delegates through wrapper helpers","Add unit tests that subscribe and dispose"],"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"}