{"record":{"id":"c5df404c48803ccf","repo":"dotnet/reactive","slug":"missing-remove-method-on-event","errorCode":null,"errorMessage":"Missing remove method on event.","messagePattern":"Missing remove method on event\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"AsyncRx.NET/System.Reactive.Async/Linq/Operators/FromEventPattern.cs","lineNumber":285,"sourceCode":"            {\n                e = targetType.GetEventEx(eventName, isStatic: true);\n                if (e == null)\n                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, \"Could not find static event '{0}' on type '{1}'.\", eventName, targetType.FullName));\n            }\n            else\n            {\n                e = targetType.GetEventEx(eventName, isStatic: false);\n                if (e == null)\n                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, \"Could not find instance event '{0}' on type '{1}'.\", eventName, targetType.FullName));\n            }\n\n            var addMethod = e.GetAddMethod();\n            var removeMethod = e.GetRemoveMethod();\n\n            if (addMethod == null)\n                throw new InvalidOperationException(\"Missing add method on event.\");\n            if (removeMethod == null)\n                throw new InvalidOperationException(\"Missing remove method on event.\");\n\n            var psa = addMethod.GetParameters();\n            if (psa.Length != 1)\n                throw new InvalidOperationException(\"The add method of an event should take one parameter.\");\n\n            var psr = removeMethod.GetParameters();\n            if (psr.Length != 1)\n                throw new InvalidOperationException(\"The remove method of an event should take one parameter.\");\n\n            var isWinRT = false;\n\n            if (addMethod.ReturnType != typeof(void))\n            {\n                isWinRT = true;\n\n                var pet = psr[0];\n                if (pet.ParameterType != addMethod.ReturnType)\n                    throw new InvalidOperationException(\"An event should either have add and remove methods that return void or an add method that returns a type compatible with the parameter type of the remove method.\");","sourceCodeStart":267,"sourceCodeEnd":303,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/AsyncRx.NET/System.Reactive.Async/Linq/Operators/FromEventPattern.cs#L267-L303","documentation":"The located EventInfo has no accessible remove accessor (GetRemoveMethod() returned null). Even if subscribing (add) is possible, the library cannot guarantee unsubscription and rejects the event with InvalidOperationException.","triggerScenarios":"Events with an add accessor but a missing/non-public remove accessor, e.g. hand-written event field declarations like 'event EventHandler E { add {} }' or trimmed assemblies where the remove method was stripped.","commonSituations":"Custom event implementations that only support add (fire-and-forget hook points), IL trimming/linker stripping unused accessors, obfuscation tools renaming/removing methods.","solutions":["Declare the event with both add and remove accessors if you control the type","Use the FromEvent overload with explicit add/remove delegate pairs if you must work with an add-only event and can manage lifetime yourself","Check assembly trimming settings and preserve the event accessors (e.g. [DynamicDependency] / linker XML)"],"exampleFix":"// before\npublic event EventHandler Tick { add { _tick += value; } } // no remove\n// after\npublic event EventHandler Tick { add { _tick += value; } remove { _tick -= value; } }","handlingStrategy":"validation","validationCode":"var ev = type.GetEvent(eventName);\nif (ev?.GetRemoveMethod() is null) throw new NotSupportedException($\"Event '{eventName}' has no accessible remove accessor.\");","typeGuard":"static bool HasRemoveAccessor(Type t, string name) => t.GetEvent(name)?.GetRemoveMethod() is not null;","tryCatchPattern":"try { var obs = AsyncObservable.FromEventPattern<TSender, TEventArgs>(target, eventName); }\ncatch (InvalidOperationException ex) { log.LogError(ex, \"Event {Event} lacks a remove accessor; cannot guarantee unsubscribe\", eventName); throw; }","preventionTips":["Always declare both add and remove accessors on custom events","Preserve accessors in trimmed builds ([DynamicDependency] or linker XML)","Test subscribe/unsubscribe lifecycle for custom events"],"tags":["csharp","reflection","event-accessors","invalid-operation"],"backgroundTag":"unsupported-operation","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"}