{"record":{"id":"1f8dfceab03d47dc","repo":"dotnet/reactive","slug":"removehandler-fromeventpattern","errorCode":null,"errorMessage":"removeHandler","messagePattern":"removeHandler","errorType":"exception","errorClass":"ArgumentNullException","httpStatus":null,"severity":"error","filePath":"AsyncRx.NET/System.Reactive.Async/Linq/Operators/FromEventPattern.cs","lineNumber":23,"sourceCode":"using System.Globalization;\nusing System.Reactive.Concurrency;\nusing System.Reactive.Disposables;\nusing System.Reflection;\nusing System.Runtime.ExceptionServices;\nusing System.Threading.Tasks;\n\nnamespace System.Reactive.Linq\n{\n    public partial class AsyncObservable\n    {\n        public static IAsyncObservable<EventPattern<object>> FromEventPattern(Action<EventHandler> addHandler, Action<EventHandler> removeHandler) => FromEventPattern(addHandler, removeHandler, GetSchedulerForCurrentContext());\n\n        public static IAsyncObservable<EventPattern<object>> FromEventPattern(Action<EventHandler> addHandler, Action<EventHandler> 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, object, EventArgs>(\n                action => new EventHandler((o, e) => action(o, e)),\n                addHandler,\n                removeHandler,\n                scheduler).Select(t => new EventPattern<object>(t.arg1, t.arg2));\n        }\n\n        public static IAsyncObservable<EventPattern<TEventArgs>> FromEventPattern<TDelegate, TEventArgs>(Action<TDelegate> addHandler, Action<TDelegate> removeHandler) => FromEventPattern<TDelegate, TEventArgs>(addHandler, removeHandler, GetSchedulerForCurrentContext());\n\n        public static IAsyncObservable<EventPattern<TEventArgs>> FromEventPattern<TDelegate, 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));","sourceCodeStart":5,"sourceCodeEnd":41,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/AsyncRx.NET/System.Reactive.Async/Linq/Operators/FromEventPattern.cs#L5-L41","documentation":"System.ArgumentNullException with param name 'removeHandler'. The FromEventPattern(addHandler, removeHandler, scheduler) overload validates all delegate arguments up-front and throws when removeHandler is null. The library requires both subscription hooks to attach and detach the underlying CLR event via the FromEvent operator, so a missing remove handler would leave no way to unsubscribe.","triggerScenarios":"Calling AsyncObservable.FromEventPattern(Action<EventHandler> addHandler, Action<EventHandler> removeHandler, IAsyncScheduler scheduler) and passing null for the removeHandler argument, e.g. FromEventPattern(h => target.Something += h, null, scheduler).","commonSituations":"Developers wire the add handler but skip the remove handler because the target has no symmetric -= API, or a refactored overload call accidentally drops one of the lambdas; also happens when the two lambdas are built dynamically and one comes back null.","solutions":["Pass a non-null removeHandler delegate, e.g. h => target.Something -= h, mirroring the add handler.","If the event truly cannot be unsubscribed, use a different FromEventPattern overload or wrap the event manually instead of passing null.","Check argument order: (addHandler, removeHandler, scheduler) — a swapped or omitted argument leaves removeHandler null.","Add an explicit null check with a clear message at your call site to fail fast before calling the library."],"exampleFix":"// before\nvar obs = AsyncObservable.FromEventPattern(\n    h => target.Something += h,\n    null,\n    scheduler);\n// after\nvar obs = AsyncObservable.FromEventPattern(\n    h => target.Something += h,\n    h => target.Something -= h,\n    scheduler);","handlingStrategy":"validation","validationCode":"if (addHandler == null) throw new ArgumentNullException(nameof(addHandler));\nif (removeHandler == null) throw new ArgumentNullException(nameof(removeHandler));\nif (scheduler == null) scheduler = AsyncScheduler.Default;","typeGuard":"static bool IsValidFromEventPatternArgs(Action<EventHandler> add, Action<EventHandler> remove, IAsyncScheduler sched)\n    => add != null && remove != null && sched != null;","tryCatchPattern":"try\n{\n    var obs = AsyncObservable.FromEventPattern(h => t.E += h, h => t.E -= h, scheduler);\n}\ncatch (ArgumentNullException ex)\n{\n    // ex.ParamName tells which argument was null; log and fail fast\n}","preventionTips":["Always pass paired add/remove lambdas for the event.","Never pass null for scheduler in the explicit overload; use the shorter overload instead.","Verify argument order (addHandler, removeHandler, scheduler).","Add unit tests covering subscription and unsubscription of the bridged event."],"tags":["argument-null","reactive-extensions","events","dotnet"],"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"}