dotnet/reactive · error · InvalidOperationException

The return type of an event delegate should be void.

Error message

The return type of an event delegate should be void.

What it means

Event delegates must return void; GetEventMethods rejects any delegate whose Invoke method returns a non-void type with this InvalidOperationException. Reactive event bridging only supports handlers that produce values through their arguments, not through a return value.

Solutions

  1. Use a delegate type returning void, e.g. EventHandler<TEventArgs> or a custom void-returning delegate matching the event
  2. If the underlying API genuinely needs a return value, wrap the event manually with Create/Subscribe and an add/remove pair that adapts the signature
  3. Verify with typeof(D).GetMethod("Invoke").ReturnType == typeof(void) before calling

Example fix

// before
delegate bool MyHandler(object sender, MyEventArgs e);
// after
delegate void MyHandler(object sender, MyEventArgs e);
Defensive patterns

Strategy: validation

Validate before calling

if (typeof(MyDelegate).GetMethod("Invoke").ReturnType != typeof(void))
    throw new InvalidOperationException("Event delegates must return void");

Type guard

static bool IsVoidDelegate(Type d)
    => d.GetMethod("Invoke")?.ReturnType == typeof(void);

Try / catch

try
{
    var obs = AsyncObservable.FromEventPattern<TSender, TEventArgs>(add, remove);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("return type"))
{
    // define/use a void-returning delegate instead
}

Prevention

When it happens

Trigger: Passing a delegate type with a non-void return (e.g. Func<object, TEventArgs, bool> or a custom delegate returning a value) to the reflection-based FromEventPattern overloads.

Common situations: Reusing functional delegates (predicate/validator style) as event handlers; custom message-bus delegate types that return a result; accidental use of Func instead of Action/EventHandler shapes.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

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

            }

            var delegateType = psa[0].ParameterType;

            var invokeMethod = delegateType.GetMethod("Invoke");

            var parameters = invokeMethod.GetParameters();

            if (parameters.Length != 2)
                throw new InvalidOperationException("The delegate type for an event conforming to the traditional event pattern should take two parameters.");

            if (!typeof(TSender).IsAssignableFrom(parameters[0].ParameterType))
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "The sender parameter of the event is not assignable to '{0}'.", typeof(TSender).FullName));

            if (!typeof(TEventArgs).IsAssignableFrom(parameters[1].ParameterType))
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, "The event arguments parameter of the event is not assignable to '{0}'.", typeof(TEventArgs).FullName));

            if (invokeMethod.ReturnType != typeof(void))
                throw new InvalidOperationException("The return type of an event delegate should be void.");

            return (addMethod, removeMethod, delegateType, isWinRT);
        }

        public static EventInfo GetEventEx(this Type type, string name, bool isStatic)
        {
            return type.GetEvent(name, isStatic ? BindingFlags.Public | BindingFlags.Static : BindingFlags.Public | BindingFlags.Instance);
        }
    }
}

View on GitHub (pinned to 94b5d5ab91)