{"record":{"id":"d78941d675ee20eb","repo":"dotnet/reactive","slug":"argumentnullexception-removehandler","errorCode":null,"errorMessage":"ArgumentNullException: removeHandler","messagePattern":"ArgumentNullException: removeHandler","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive.WindowsRuntime/System.Reactive.Linq/WindowsObservable.Events.cs","lineNumber":38,"sourceCode":"        /// Converts a typed event, conforming to the standard event pattern, to an observable sequence.\n        /// </summary>\n        /// <typeparam name=\"TSender\">The type of the sender that raises the event.</typeparam>\n        /// <typeparam name=\"TResult\">The type of the event data generated by the event.</typeparam>\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        /// <returns>The observable sequence that contains data representations of invocations of the underlying typed event.</returns>\n        /// <exception cref=\"ArgumentNullException\"><paramref name=\"addHandler\"/> or <paramref name=\"removeHandler\"/> is null.</exception>\n        /// <seealso cref=\"ToEventPattern\"/>\n        public static IObservable<EventPattern<TSender, TResult>> FromEventPattern<TSender, TResult>(Action<TypedEventHandler<TSender, TResult>> addHandler, Action<TypedEventHandler<TSender, TResult>> removeHandler)\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 Observable.Create<EventPattern<TSender, TResult>>(observer =>\n            {\n                var h = new TypedEventHandler<TSender, TResult>((sender, args) =>\n                {\n                    observer.OnNext(new EventPattern<TSender, TResult>(sender, args));\n                });\n\n                addHandler(h);\n\n                return () =>\n                {\n                    removeHandler(h);\n                };\n            });\n        }\n","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive.WindowsRuntime/System.Reactive.Linq/WindowsObservable.Events.cs#L20-L56","documentation":"In FromEventPattern<TSender, TResult>(addHandler, removeHandler), after validating addHandler the method validates removeHandler and throws ArgumentNullException(\"removeHandler\") when it is null. Both delegates are required so the observable can attach on Subscribe and detach on Dispose.","triggerScenarios":"Calling Observable.FromEventPattern<TSender, TResult>(addHandler, null) — a null remove delegate, commonly from reflection lookups that failed to find the remove accessor, or building add/remove pairs where only add was supplied.","commonSituations":"Reflection-based wiring missing the remove accessor (GetRemoveMethod returned null); event objects exposed through wrappers where unsubscribe isn't implemented and null was passed instead; copy-paste errors omitting the second delegate.","solutions":["Provide the real remove accessor: h => target.Event -= h.","If reflection fails to find the remove method, surface a descriptive error rather than passing null.","Never substitute null for unsubscribe; a no-op remove delegate (h => { }) at least avoids the throw, but prefer correct detachment to avoid leaks."],"exampleFix":"// before\nObservable.FromEventPattern<Button, object>(\n    h => button.Click += h,\n    null); // throws\n// after\nObservable.FromEventPattern<Button, object>(\n    h => button.Click += h,\n    h => button.Click -= h);","handlingStrategy":"validation","validationCode":"if (removeHandler == null)\n    throw new InvalidOperationException(\"removeHandler required to detach on dispose\");","typeGuard":"bool HasRemove<TH>(Action<TH>? remove) => remove is not null;","tryCatchPattern":"try { obs = Observable.FromEventPattern<TSender, TResult>(add, remove); }\ncatch (ArgumentNullException ex) when (ex.ParamName == \"removeHandler\") { log.Error(\"remove accessor missing; subscription would leak\"); }","preventionTips":["Always pair add and remove: h => e += h with h => e -= h","If reflection can't find a remove accessor, treat it as a wiring bug, not an optional value","Review wrappers around legacy events to guarantee unsubscribe support"],"tags":["rx","null-argument","windows-runtime","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"}