dotnet/reactive · error · InvalidOperationException

Missing remove method on event.

Error message

Missing remove method on event.

What it means

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.

Solutions

  1. Declare the event with both add and remove accessors if you control the type
  2. Use the FromEvent overload with explicit add/remove delegate pairs if you must work with an add-only event and can manage lifetime yourself
  3. Check assembly trimming settings and preserve the event accessors (e.g. [DynamicDependency] / linker XML)

Example fix

// before
public event EventHandler Tick { add { _tick += value; } } // no remove
// after
public event EventHandler Tick { add { _tick += value; } remove { _tick -= value; } }
Defensive patterns

Strategy: validation

Validate before calling

var ev = type.GetEvent(eventName);
if (ev?.GetRemoveMethod() is null) throw new NotSupportedException($"Event '{eventName}' has no accessible remove accessor.");

Type guard

static bool HasRemoveAccessor(Type t, string name) => t.GetEvent(name)?.GetRemoveMethod() is not null;

Try / catch

try { var obs = AsyncObservable.FromEventPattern<TSender, TEventArgs>(target, eventName); }
catch (InvalidOperationException ex) { log.LogError(ex, "Event {Event} lacks a remove accessor; cannot guarantee unsubscribe", eventName); throw; }

Prevention

When it happens

Trigger: 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.

Common situations: Custom event implementations that only support add (fire-and-forget hook points), IL trimming/linker stripping unused accessors, obfuscation tools renaming/removing methods.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at AsyncRx.NET/System.Reactive.Async/Linq/Operators/FromEventPattern.cs:285

            {
                e = targetType.GetEventEx(eventName, isStatic: true);
                if (e == null)
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Could not find static event '{0}' on type '{1}'.", eventName, targetType.FullName));
            }
            else
            {
                e = targetType.GetEventEx(eventName, isStatic: false);
                if (e == null)
                    throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "Could not find instance event '{0}' on type '{1}'.", eventName, targetType.FullName));
            }

            var addMethod = e.GetAddMethod();
            var removeMethod = e.GetRemoveMethod();

            if (addMethod == null)
                throw new InvalidOperationException("Missing add method on event.");
            if (removeMethod == null)
                throw new InvalidOperationException("Missing remove method on event.");

            var psa = addMethod.GetParameters();
            if (psa.Length != 1)
                throw new InvalidOperationException("The add method of an event should take one parameter.");

            var psr = removeMethod.GetParameters();
            if (psr.Length != 1)
                throw new InvalidOperationException("The remove method of an event should take one parameter.");

            var isWinRT = false;

            if (addMethod.ReturnType != typeof(void))
            {
                isWinRT = true;

                var pet = psr[0];
                if (pet.ParameterType != addMethod.ReturnType)
                    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.");

View on GitHub (pinned to 94b5d5ab91)