dotnet/reactive · error · InvalidOperationException

Strings_Linq.EVENT_REMOVE_METHOD_SHOULD_TAKE_ONE_PARAMETER

Error message

Strings_Linq.EVENT_REMOVE_METHOD_SHOULD_TAKE_ONE_PARAMETER

What it means

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.

Solutions

  1. Fix the event's remove accessor so it takes exactly one parameter of the same delegate type as the add accessor.
  2. Verify you passed the correct event name / addMethod-removeMethod pair to FromEventPattern; a wrong pair can pick up an invalid accessor.
  3. If the event is truly non-standard, do not use FromEventPattern; wrap the event manually with Observable.Create and a custom unsubscribe.

Example fix

// before
public event EventHandler E { add { } remove { } } // remove has 0 params
// after
private EventHandler _e;
public event EventHandler E { add { _e += value; } remove { _e -= value; } }
Defensive patterns

Strategy: validation

Validate before calling

var add = type.GetMethod("add_" + name);
var remove = type.GetMethod("remove_" + name);
if (remove == null || remove.GetParameters().Length != 1)
    throw new ArgumentException($"Event {name}: remove accessor must take exactly one parameter");

Try / catch

try
{
    var source = Observable.FromEventPattern(target, eventName);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("remove method"))
{
    // fall back to manual Observable.Create wiring
}

Prevention

When it happens

Trigger: Calling Observable.FromEventPattern/FromEvent with an event whose remove method (e.g. remove_EventName) has zero or multiple parameters, discovered via ReflectionUtils.GetEventMethods.

Common situations: 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.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of dotnet/reactive@94b5d5ab91 (2026-09-15). Data as JSON: /api/errors/365c43e238f0de96. Report an issue: GitHub.

Appendix: source

Thrown at Rx.NET/Source/src/System.Reactive/Internal/ReflectionUtils.cs:53

            else
            {
                e = targetType.GetEvent(eventName, BindingFlags.Public | BindingFlags.Instance)
                    ?? throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Strings_Linq.COULD_NOT_FIND_INSTANCE_EVENT, eventName, targetType.FullName));
            }

            addMethod = e.GetAddMethod() ?? throw new InvalidOperationException(Strings_Linq.EVENT_MISSING_ADD_METHOD);
            removeMethod = e.GetRemoveMethod() ?? throw new InvalidOperationException(Strings_Linq.EVENT_MISSING_REMOVE_METHOD);

            var psa = addMethod.GetParameters();
            if (psa.Length != 1)
            {
                throw new InvalidOperationException(Strings_Linq.EVENT_ADD_METHOD_SHOULD_TAKE_ONE_PARAMETER);
            }

            var psr = removeMethod.GetParameters();
            if (psr.Length != 1)
            {
                throw new InvalidOperationException(Strings_Linq.EVENT_REMOVE_METHOD_SHOULD_TAKE_ONE_PARAMETER);
            }

            isWinRT = false;

            if (IsWinRTEventRegistrationTokenType(addMethod.ReturnType))
            {
                isWinRT = true;

                var pet = psr[0];
                if (IsWinRTEventRegistrationTokenType(pet.ParameterType))
                {
                    throw new InvalidOperationException(Strings_Linq.EVENT_WINRT_REMOVE_METHOD_SHOULD_TAKE_ERT);
                }
            }

            delegateType = psa[0].ParameterType;

            var invokeMethod = delegateType.GetMethod("Invoke")!; // NB: Delegates always have an Invoke method.

View on GitHub (pinned to 94b5d5ab91)