{"record":{"id":"365c43e238f0de96","repo":"dotnet/reactive","slug":"strings-linq-event-remove-method-should-take-one-parameter","errorCode":null,"errorMessage":"Strings_Linq.EVENT_REMOVE_METHOD_SHOULD_TAKE_ONE_PARAMETER","messagePattern":"Strings_Linq\\.EVENT_REMOVE_METHOD_SHOULD_TAKE_ONE_PARAMETER","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"Rx.NET/Source/src/System.Reactive/Internal/ReflectionUtils.cs","lineNumber":53,"sourceCode":"            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;\n\n                var pet = psr[0];\n                if (IsWinRTEventRegistrationTokenType(pet.ParameterType))\n                {\n                    throw new InvalidOperationException(Strings_Linq.EVENT_WINRT_REMOVE_METHOD_SHOULD_TAKE_ERT);\n                }\n            }\n\n            delegateType = psa[0].ParameterType;\n\n            var invokeMethod = delegateType.GetMethod(\"Invoke\")!; // NB: Delegates always have an Invoke method.","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/dotnet/reactive/blob/94b5d5ab912789f5abe9a72138a25bbd716fe59c/Rx.NET/Source/src/System.Reactive/Internal/ReflectionUtils.cs#L35-L71","documentation":"GetEventMethods validates that an event's remove accessor (remove_X) takes exactly one parameter, matching the add accessor. Rx's FromEventPattern uses reflection to wire observable subscriptions to CLR events, and a remove method with a parameter count other than 1 cannot be invoked with a single subscription token. The library throws InvalidOperationException to fail fast on events that do not follow the standard event pattern.","triggerScenarios":"Calling Observable.FromEventPattern/FromEvent with an event whose remove method (e.g. remove_EventName) has zero or multiple parameters, discovered via ReflectionUtils.GetEventMethods.","commonSituations":"Reflecting over custom or third-party classes with hand-written add/remove accessors; COM or WinRT interop types with non-standard remove signatures; code-generation output that desynchronized add and remove accessors.","solutions":["Fix the event's remove accessor so it takes exactly one parameter of the same delegate type as the add accessor.","Verify you passed the correct event name / addMethod-removeMethod pair to FromEventPattern; a wrong pair can pick up an invalid accessor.","If the event is truly non-standard, do not use FromEventPattern; wrap the event manually with Observable.Create and a custom unsubscribe."],"exampleFix":"// before\npublic event EventHandler E { add { } remove { } } // remove has 0 params\n// after\nprivate EventHandler _e;\npublic event EventHandler E { add { _e += value; } remove { _e -= value; } }","handlingStrategy":"validation","validationCode":"var add = type.GetMethod(\"add_\" + name);\nvar remove = type.GetMethod(\"remove_\" + name);\nif (remove == null || remove.GetParameters().Length != 1)\n    throw new ArgumentException($\"Event {name}: remove accessor must take exactly one parameter\");","typeGuard":null,"tryCatchPattern":"try\n{\n    var source = Observable.FromEventPattern(target, eventName);\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"remove method\"))\n{\n    // fall back to manual Observable.Create wiring\n}","preventionTips":["Keep add/remove accessors symmetric: one parameter, same delegate type.","Prefer compiler-generated events (field-like events) over hand-written accessors.","Unit-test reflection-based event wiring on the target types."],"tags":["reflection","events","invalid-argument"],"backgroundTag":"invalid-argument-format","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"}