{"record":{"id":"4d7a9f55bdf25d2b","repo":"dotnet/reactive","slug":"nameof-removehandler","errorCode":null,"errorMessage":"nameof(removeHandler)","messagePattern":"nameof\\(removeHandler\\)","errorType":"validation","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"AsyncRx.NET/System.Reactive.Async/Linq/Operators/FromEventPattern.cs","lineNumber":71,"sourceCode":"                throw new ArgumentNullException(nameof(conversion));\n            if (addHandler == null)\n                throw new ArgumentNullException(nameof(addHandler));\n            if (removeHandler == null)\n                throw new ArgumentNullException(nameof(removeHandler));\n            if (scheduler == null)\n                throw new ArgumentNullException(nameof(scheduler));\n\n            return FromEvent<TDelegate, object, TEventArgs>(action => conversion(new EventHandler<TEventArgs>((o, e) => action(o, e))), addHandler, removeHandler, scheduler).Select(t => new EventPattern<TEventArgs>(t.arg1, t.arg2));\n        }\n\n        public static IAsyncObservable<EventPattern<TSender, TEventArgs>> FromEventPattern<TDelegate, TSender, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler) => FromEventPattern<TDelegate, TSender, TEventArgs>(addHandler, removeHandler, GetSchedulerForCurrentContext());\n\n        public static IAsyncObservable<EventPattern<TSender, TEventArgs>> FromEventPattern<TDelegate, TSender, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler, IAsyncScheduler scheduler)\n        {\n            if (addHandler == null)\n                throw new ArgumentNullException(nameof(addHandler));\n            if (removeHandler == null)\n                throw new ArgumentNullException(nameof(removeHandler));\n            if (scheduler == null)\n                throw new ArgumentNullException(nameof(scheduler));\n\n            return FromEvent<TDelegate, TSender, TEventArgs>(action => ConvertDelegate<Action<TSender, TEventArgs>, TDelegate>(action), addHandler, removeHandler, scheduler).Select(t => new EventPattern<TSender, TEventArgs>(t.arg1, t.arg2));\n        }\n\n        public static IAsyncObservable<EventPattern<TEventArgs>> FromEventPattern<TEventArgs>(Action<EventHandler<TEventArgs>> addHandler, Action<EventHandler<TEventArgs>> removeHandler) => FromEventPattern<TEventArgs>(addHandler, removeHandler, GetSchedulerForCurrentContext());\n\n        public static IAsyncObservable<EventPattern<TEventArgs>> FromEventPattern<TEventArgs>(Action<EventHandler<TEventArgs>> addHandler, Action<EventHandler<TEventArgs>> removeHandler, IAsyncScheduler scheduler)\n        {\n            if (addHandler == null)\n                throw new ArgumentNullException(nameof(addHandler));\n            if (removeHandler == null)\n                throw new ArgumentNullException(nameof(removeHandler));\n            if (scheduler == null)\n                throw new ArgumentNullException(nameof(scheduler));\n\n            return FromEvent<EventHandler<TEventArgs>, object, TEventArgs>(action => ConvertDelegate<Action<object, TEventArgs>, EventHandler<TEventArgs>>(action), addHandler, removeHandler, scheduler).Select(t => new EventPattern<TEventArgs>(t.arg1, t.arg2));","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/AsyncRx.NET/System.Reactive.Async/Linq/Operators/FromEventPattern.cs#L53-L89","documentation":"This is an ArgumentNullException thrown synchronously by the public AsyncRx FromEventPattern<TDelegate, TSender, TEventArgs>(addHandler, removeHandler, scheduler) overload when the removeHandler delegate is null. The library validates all delegate/scheduler arguments up front because the returned observable must be able to detach the handler on subscription disposal; without a remove handler the operator cannot guarantee cleanup. The nameof(removeHandler) in the message names the exact offending parameter.","triggerScenarios":"Calling AsyncObservable.FromEventPattern<TDelegate, TSender, TEventArgs> with (addHandler, null, scheduler) — e.g. passing a variable that was never assigned, or passing only the add delegate in a refactor that switched from a two-delegate to a null-tolerant API.","commonSituations":"Developers wiring .NET events to Rx who copy an addHandler expression and forget the matching removeHandler; reflection-built delegates where MethodInfo.CreateDelegate returned null; DI-registered handler accessors that resolved to null in tests.","solutions":["Pass a non-null removeHandler delegate that detaches the same handler added by addHandler.","If the event only supports add (no remove), use the FromEvent overload or a Subject-based bridge instead of FromEventPattern.","Guard the call site with a null check and fail fast with a meaningful message before constructing the observable."],"exampleFix":"// before\nvar obs = AsyncObservable.FromEventPattern<Action, object, EventArgs>(h => target.Fired += h, null, scheduler);\n// after\nvar obs = AsyncObservable.FromEventPattern<Action, object, EventArgs>(h => target.Fired += h, h => target.Fired -= h, scheduler);","handlingStrategy":"validation","validationCode":"if (addHandler is null) throw new ArgumentNullException(nameof(addHandler));\nif (removeHandler is null) throw new ArgumentNullException(nameof(removeHandler));","typeGuard":"bool HasHandlerPair(A a, R r) => a is not null && r is not null;","tryCatchPattern":"try\n{\n    var obs = AsyncObservable.FromEventPattern<Action, object, EventArgs>(addHandler, removeHandler, scheduler);\n}\ncatch (ArgumentNullException ex) when (ex.ParamName == nameof(removeHandler))\n{\n    logger.LogError(ex, \"removeHandler must be provided to detach on unsubscribe\");\n}","preventionTips":["Always pair h => e.Add(h) with h => e.Remove(h) in the same expression.","Never pass null delegate literals to FromEventPattern; use the target/eventName overloads if you want reflection to supply both.","Add unit tests that subscribe and dispose each event bridge to surface missing remove handlers."],"tags":["argument-null","csharp","reactive-extensions","fromeventpattern"],"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"}