dotnet/reactive · error · InvalidOperationException

Missing add method on event.

Error message

Missing add method on event.

What it means

The located EventInfo has no accessible add accessor (GetAddMethod() returned null), so the library cannot subscribe to it and throws InvalidOperationException. An event without an add method cannot have a handler attached via reflection.

Solutions

  1. Subscribe via a public event with standard add/remove accessors
  2. Use the delegate-based FromEvent overload with explicit addHandler/removeHandler delegates referencing the accessors directly
  3. Make the add accessor public (or use BindingFlags-friendly accessibility) if you control the type

Example fix

// before
var obs = AsyncObservable.FromEventPattern<object, EventArgs>(type, "HiddenEvent"); // explicit interface event
// after
IFoo f = GetFoo();
var obs = AsyncObservable.FromEvent<EventHandler, EventArgs>(
    h => f.HiddenEvent += h, h => f.HiddenEvent -= h);
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

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

Try / catch

try { var obs = AsyncObservable.FromEventPattern<TSender, TEventArgs>(target, eventName); }
catch (InvalidOperationException ex) { // fall back to delegate-based FromEvent with explicit handlers
  log.LogWarning(ex, "No add accessor for {Event}; using manual subscription", eventName); }

Prevention

When it happens

Trigger: Reflecting over an event whose add accessor is non-public, abstract, or otherwise unavailable to the library (e.g. an explicitly implemented interface event or a compiler-generated event with restricted accessors).

Common situations: Events declared on internal/explicit interface implementations, events from obfuscated or trimmed assemblies (linker removed the accessor), or platform-projection events with unusual metadata.

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/9b166a5f84fae8e4. Report an issue: GitHub.

Appendix: source

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

            if (target == null)
            {
                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];

View on GitHub (pinned to 94b5d5ab91)