{"record":{"id":"6d3fa8f6690db672","repo":"dotnet/reactive","slug":"removehandler-observable-events","errorCode":null,"errorMessage":"removeHandler","messagePattern":"removeHandler","errorType":"exception","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Linq/Observable.Events.cs","lineNumber":1182,"sourceCode":"        /// 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>\n        /// <param name=\"removeHandler\">Action that detaches the given event handler from the underlying .NET event.</param>\n        /// <param name=\"scheduler\">The scheduler to run the add and remove event handler logic on.</param>\n        /// <returns>The observable sequence that contains the event argument objects passed to the invocations of the underlying .NET event.</returns>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"conversion\"/> or <paramref name=\"addHandler\"/> or <paramref name=\"removeHandler\"/> or <paramref name=\"scheduler\"/> is null.</exception>\n        /// <remarks>","sourceCodeStart":1164,"sourceCodeEnd":1200,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Linq/Observable.Events.cs#L1164-L1200","documentation":"Observable.FromEvent<TDelegate,TEventArgs> throws ArgumentNullException when removeHandler is null. removeHandler unsubscribes the handler when the observable subscription is disposed; Rx validates it up-front to guarantee proper cleanup.","triggerScenarios":"Calling FromEvent(conversion, addHandler, null), e.g. when only subscription logic was written and unsubscribe was left as null or forgotten.","commonSituations":"Generating add/remove pairs with T4/templates where one side failed; passing the same lambda for both but one argument left null; incomplete adapter code around legacy events.","solutions":["Pass a matching remove delegate, e.g. h => target.MyEvent -= h (must mirror addHandler).","If the API truly lacks removal, consider wrapping with a no-op: h => { } only if leaking a subscription is acceptable — normally not.","Verify argument order: conversion, addHandler, removeHandler.","Unit-test dispose of the subscription to confirm the remove handler is supplied and correct."],"exampleFix":"// before\nObservable.FromEvent<EventHandler, EventArgs>(conv, h => target.MyEvent += h, null);\n// after\nObservable.FromEvent<EventHandler, EventArgs>(conv, h => target.MyEvent += h, h => target.MyEvent -= h);","handlingStrategy":"validation","validationCode":"if (removeHandler is null) throw new ArgumentNullException(nameof(removeHandler));","typeGuard":"bool HasRemove<TDelegate>(Action<TDelegate> remove) => remove is not null;","tryCatchPattern":null,"preventionTips":["Always mirror addHandler with removeHandler in the same code block","Treat a null removeHandler as a bug, never a legitimate value","Test that disposing the subscription detaches the handler","Generate add/remove pairs via a helper method to keep them in sync"],"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"}