elsa-workflows/elsa-core · error · InvalidOperationException

Multiple Invoke methods were found. Use either Invoke or…

Error message

Multiple Invoke methods were found. Use either Invoke or InvokeAsync.

What it means

MiddlewareHelpers.GetInvokeMethod reflects over a middleware type to locate its single public instance Invoke or InvokeAsync method, which the mediator pipeline uses to compose middleware. This error is thrown when the type exposes more than one public method named Invoke or InvokeAsync (overloads count). The library deliberately rejects ambiguous middleware contracts.

Solutions

  1. Keep exactly one public instance method named Invoke or InvokeAsync; rename or make the other overload private/internal.
  2. If you need to handle multiple context types, split into separate middleware classes, one per context type.
  3. If the extra method is a helper, rename it (e.g. InvokeCoreAsync) so it no longer collides with the pipeline entry-point name.
  4. Make helper overloads non-public so reflection (BindingFlags.Public | BindingFlags.Instance) only finds the single entry point.

Example fix

// before
public class MyMiddleware : IMiddleware
{
    public ValueTask InvokeAsync(CommandContext context, CommandMiddlewareDelegate next) => ...;
    public ValueTask InvokeAsync(OtherContext context, OtherDelegate next) => ...; // ambiguous
}

// after
public class MyMiddleware : IMiddleware
{
    public ValueTask InvokeAsync(CommandContext context, CommandMiddlewareDelegate next) => ...;
    private ValueTask InvokeOtherAsync(OtherContext context, OtherDelegate next) => ...;
Defensive patterns

Strategy: validation

Validate before calling

// Before registering middleware, check it has exactly one Invoke/InvokeAsync entry point:
var names = new[] { "Invoke", "InvokeAsync" };
var count = middlewareType.GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public)
    .Count(m => names.Contains(m.Name, StringComparer.Ordinal));
if (count != 1)
    throw new InvalidOperationException($"{middlewareType.Name} must define exactly one public Invoke or InvokeAsync method (found {count}).");

Type guard

static bool HasSingleInvokeEntryPoint(Type t) =>
    t.GetMethods(BindingFlags.Instance | BindingFlags.Public)
     .Count(m => m.Name is "Invoke" or "InvokeAsync") == 1;

Try / catch

try
{
    pipeline.AddMiddleware(typeof(MyMiddleware));
}
catch (InvalidOperationException ex) when (ex.Message.Contains("Multiple Invoke methods"))
{
    logger.LogError(ex, "Middleware {Type} has overloads; keep a single entry point", typeof(MyMiddleware).Name);
    throw;
}

Prevention

When it happens

Trigger: Defining a middleware class with overloaded InvokeAsync methods (e.g. InvokeAsync(MyContext) and InvokeAsync(OtherContext)); a middleware with both an Invoke and an InvokeAsync method; overloads differing only by parameter types all match by name and are counted together.

Common situations: Developers add a second InvokeAsync overload for a different context type or for convenience/default parameters; refactoring renames one method but leaves the old overload; generic middleware written with multiple public entry points; copy-pasting middleware code that kept an unused old method.

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 elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/dc48f95714270c87. Report an issue: GitHub.

Appendix: source

Thrown at src/common/Elsa.Mediator/Middleware/MiddlewareHelpers.cs:26

public static class MiddlewareHelpers
{
    /// <summary>
    /// Gets the Invoke or InvokeAsync method from the middleware type.
    /// </summary>
    /// <param name="middleware">The middleware type.</param>
    /// <returns>The Invoke or InvokeAsync method.</returns>
    /// <exception cref="InvalidOperationException">Thrown when the Invoke or InvokeAsync method cannot be found or the return type is not Task or ValueTask.</exception>
    public static MethodInfo GetInvokeMethod(Type middleware)
    {
        const string invokeMethodName = "Invoke";
        const string invokeAsyncMethodName = "InvokeAsync";
        var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public);
        var invokeMethods = methods.Where(m => string.Equals(m.Name, invokeMethodName, StringComparison.Ordinal) || string.Equals(m.Name, invokeAsyncMethodName, StringComparison.Ordinal)).ToArray();

        switch (invokeMethods.Length)
        {
            case > 1:
                throw new InvalidOperationException("Multiple Invoke methods were found. Use either Invoke or InvokeAsync.");
            case 0:
                throw new InvalidOperationException("No Invoke methods were found. Use either Invoke or InvokeAsync");
        }

        var methodInfo = invokeMethods[0];

        if (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType) && !typeof(ValueTask).IsAssignableFrom(methodInfo.ReturnType))
            throw new InvalidOperationException($"The {methodInfo.Name} method must return Task or ValueTask");

        return methodInfo;
    }
}

View on GitHub (pinned to fe9217bdfa)