elsa-workflows/elsa-core · critical · InvalidOperationException

The method must return Task or ValueTask

Error message

The {methodInfo.Name} method must return Task or ValueTask

What it means

Elsa's MiddlewareHelpers.GetInvokeMethod locates the 'Invoke' or 'InvokeAsync' method on a middleware/custom component type and requires it to return Task or ValueTask so it can be awaited asynchronously. If the discovered method returns void or another non-awaitable type, this InvalidOperationException is thrown. It enforces the async contract of Elsa middleware invoke methods.

Solutions

  1. Change the middleware's Invoke/InvokeAsync return type to Task or ValueTask.
  2. If the body has no awaits, return Task.CompletedTask (or make the method async and await nothing is not allowed; prefer ValueTask.FromResult(default)).
  3. Re-check the middleware registration to ensure the type passed to the pipeline is the one with the corrected signature.

Example fix

// before
public void InvokeAsync(WorkflowMiddlewareContext context)
{
    // sync work
}

// after
public async Task InvokeAsync(WorkflowMiddlewareContext context)
{
    // async work
}
Defensive patterns

Strategy: validation

Validate before calling

var method = typeof(MyMiddleware).GetMethod("InvokeAsync");
if (method == null || !typeof(Task).IsAssignableFrom(method.ReturnType) && !typeof(ValueTask).IsAssignableFrom(method.ReturnType))
    throw new InvalidOperationException("Middleware Invoke/InvokeAsync must return Task or ValueTask");

Prevention

When it happens

Trigger: Registering a middleware (or workflow component) whose Invoke/InvokeAsync method is declared with a 'void' or synchronous return type, causing typeof(Task).IsAssignableFrom and typeof(ValueTask).IsAssignableFrom checks to both fail at GetInvokeMethod (src/modules/Elsa.Workflows.Core/Pipelines/MiddlewareHelpers.cs:25).

Common situations: Hand-written middleware converted from synchronous code; developers following older Elsa 2.x middleware examples where synchronous signatures were allowed; copy-paste middleware samples renamed to InvokeAsync but left returning void.

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

Appendix: source

Thrown at src/modules/Elsa.Workflows.Core/Pipelines/MiddlewareHelpers.cs:25

    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)