{"record":{"id":"2395a72befcf0e51","repo":"dotnet/reactive","slug":"strings-linq-event-missing-remove-method","errorCode":null,"errorMessage":"Strings_Linq.EVENT_MISSING_REMOVE_METHOD","messagePattern":"Strings_Linq\\.EVENT_MISSING_REMOVE_METHOD","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Internal/ReflectionUtils.cs","lineNumber":42,"sourceCode":"    [RequiresUnreferencedCode(Constants_Core.EventReflectionTrimIncompatibilityMessage)]\n#endif\n        public static void GetEventMethods<TSender, TEventArgs>(Type targetType, object? target, string eventName, out MethodInfo addMethod, out MethodInfo removeMethod, out Type delegateType, out bool isWinRT)\n        {\n            EventInfo? e;\n\n            if (target == null)\n            {\n                e = targetType.GetEvent(eventName, BindingFlags.Public | BindingFlags.Static)\n                    ?? throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings_Linq.COULD_NOT_FIND_STATIC_EVENT, eventName, targetType.FullName));\n            }\n            else\n            {\n                e = targetType.GetEvent(eventName, BindingFlags.Public | BindingFlags.Instance)\n                    ?? throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings_Linq.COULD_NOT_FIND_INSTANCE_EVENT, eventName, targetType.FullName));\n            }\n\n            addMethod = e.GetAddMethod() ?? throw new InvalidOperationException(Strings_Linq.EVENT_MISSING_ADD_METHOD);\n            removeMethod = e.GetRemoveMethod() ?? throw new InvalidOperationException(Strings_Linq.EVENT_MISSING_REMOVE_METHOD);\n\n            var psa = addMethod.GetParameters();\n            if (psa.Length != 1)\n            {\n                throw new InvalidOperationException(Strings_Linq.EVENT_ADD_METHOD_SHOULD_TAKE_ONE_PARAMETER);\n            }\n\n            var psr = removeMethod.GetParameters();\n            if (psr.Length != 1)\n            {\n                throw new InvalidOperationException(Strings_Linq.EVENT_REMOVE_METHOD_SHOULD_TAKE_ONE_PARAMETER);\n            }\n\n            isWinRT = false;\n\n            if (IsWinRTEventRegistrationTokenType(addMethod.ReturnType))\n            {\n                isWinRT = true;","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Internal/ReflectionUtils.cs#L24-L60","documentation":"Symmetric to 2577: GetEventMethods also requires a remove accessor to support unsubscription. If e.GetRemoveMethod() returns null (remove accessor non-public or absent), it throws InvalidOperationException 'Event missing remove method'.","triggerScenarios":"FromEventPattern against an event with a public add but non-public/missing remove accessor (asymmetric custom accessors).","commonSituations":"Custom event implementations where remove was made private; events whose remove accessor is only in an explicitly implemented non-public interface; reflection-only loading scenarios.","solutions":["Make the event's remove accessor public.","Use the FromEventPattern overload taking explicit addHandler/removeHandler delegates so remove is under your control.","Manually manage lifetime: subscribe via add accessor and never rely on library-driven removal — though prefer fixing visibility."],"exampleFix":"// before\npublic event EventHandler E { add { _e += value; } private remove { _e -= value; } }\n// after\npublic event EventHandler E { add { _e += value; } remove { _e -= value; } }","handlingStrategy":"validation","validationCode":"var evt = targetType.GetEvent(eventName, BindingFlags.Public | BindingFlags.Instance);\nif (evt?.GetRemoveMethod() == null) throw new NotSupportedException($\"Event '{eventName}' has no public remove accessor\");","typeGuard":"static bool HasPublicRemove(Type t, string name) => t.GetEvent(name)?.GetRemoveMethod() != null;","tryCatchPattern":"try { Observable.FromEventPattern(target, eventName, targetType); } catch (InvalidOperationException ex) when (ex.Message.Contains(\"remove method\")) { /* use explicit add/remove overloads */ }","preventionTips":["Keep add and remove accessors symmetrically public.","Inspect custom events with ildasm/reflection before using string-based FromEventPattern.","Prefer explicit addHandler/removeHandler lambda overloads when accessors are unusual."],"tags":["reflection","event","accessor"],"backgroundTag":"missing-dependency","analyzedSha":"94b5d5ab912789f5abe9a72138a25bbd716fe59c","analyzedAt":"2026-09-15T02:26:24.759Z","contentChangedAt":"2026-09-15T02:26:24.759Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}