{"record":{"id":"7292f3b99757894a","repo":"elsa-workflows/elsa-core","slug":"the-methodinfo-name-method-must-return-task-or-valuetask","errorCode":null,"errorMessage":"The {methodInfo.Name} method must return Task or ValueTask","messagePattern":"The (.+?) method must return Task or ValueTask","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/common/Elsa.Mediator/Middleware/MiddlewareHelpers.cs","lineNumber":34,"sourceCode":"    public static MethodInfo GetInvokeMethod(Type middleware)\n    {\n        const string invokeMethodName = \"Invoke\";\n        const string invokeAsyncMethodName = \"InvokeAsync\";\n        var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public);\n        var invokeMethods = methods.Where(m => string.Equals(m.Name, invokeMethodName, StringComparison.Ordinal) || string.Equals(m.Name, invokeAsyncMethodName, StringComparison.Ordinal)).ToArray();\n\n        switch (invokeMethods.Length)\n        {\n            case > 1:\n                throw new InvalidOperationException(\"Multiple Invoke methods were found. Use either Invoke or InvokeAsync.\");\n            case 0:\n                throw new InvalidOperationException(\"No Invoke methods were found. Use either Invoke or InvokeAsync\");\n        }\n\n        var methodInfo = invokeMethods[0];\n\n        if (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType) && !typeof(ValueTask).IsAssignableFrom(methodInfo.ReturnType))\n            throw new InvalidOperationException($\"The {methodInfo.Name} method must return Task or ValueTask\");\n\n        return methodInfo;\n    }\n}","sourceCodeStart":16,"sourceCodeEnd":38,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/common/Elsa.Mediator/Middleware/MiddlewareHelpers.cs#L16-L38","documentation":"After locating the middleware's Invoke/InvokeAsync method, GetInvokeMethod validates that it returns Task or ValueTask, because the pipeline awaits middleware via reflection. This error is thrown when the middleware entry-point method returns something else (void, sync result, custom type), which would break the awaitable pipeline contract.","triggerScenarios":"Writing `public void InvokeAsync(...)` or `public string Invoke(...)`; returning a plain value or a non-awaitable custom wrapper type; using an older sync middleware signature in the async pipeline; a method named InvokeAsync that returns a custom builder type instead of Task/ValueTask.","commonSituations":"Porting synchronous middleware from another mediator library; writing middleware with side effects only and instinctively returning void; auto-generating middleware stubs with wrong return types; generic wrappers returning Task<T>-derived custom types that no longer satisfy the direct Task/ValueTask check.","solutions":["Change the middleware method's return type to Task (use async/await and return Task) or ValueTask.","If no async work exists, return Task.CompletedTask from a Task-returning method instead of void.","Wrap existing sync logic: `public async ValueTask InvokeAsync(Ctx ctx, Del next) { DoWork(); await next(ctx); }`.","Ensure the method name is exactly Invoke or InvokeAsync and it is a public instance method."],"exampleFix":"// before\npublic void InvokeAsync(CommandContext context, CommandMiddlewareDelegate next)\n{\n    next(context);\n}\n\n// after\npublic async ValueTask InvokeAsync(CommandContext context, CommandMiddlewareDelegate next)\n{\n    await next(context);\n}","handlingStrategy":"validation","validationCode":"// Validate the middleware entry point returns Task or ValueTask before registering:\nvar invoke = middlewareType.GetMethods(BindingFlags.Instance | BindingFlags.Public)\n    .FirstOrDefault(m => m.Name is \"Invoke\" or \"InvokeAsync\");\nif (invoke != null &&\n    !typeof(Task).IsAssignableFrom(invoke.ReturnType) &&\n    !typeof(ValueTask).IsAssignableFrom(invoke.ReturnType))\n    throw new InvalidOperationException($\"{middlewareType.Name}.{invoke.Name} must return Task or ValueTask.\");","typeGuard":"static bool IsAwaitableMiddleware(Type t)\n{\n    var m = t.GetMethods(BindingFlags.Instance | BindingFlags.Public)\n             .FirstOrDefault(x => x.Name is \"Invoke\" or \"InvokeAsync\");\n    return m != null && (typeof(Task).IsAssignableFrom(m.ReturnType) || typeof(ValueTask).IsAssignableFrom(m.ReturnType));\n}","tryCatchPattern":"try\n{\n    pipeline.AddMiddleware(typeof(MyMiddleware));\n}\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"must return Task or ValueTask\"))\n{\n    logger.LogError(ex, \"{Type} middleware entry point is not awaitable\", typeof(MyMiddleware).Name);\n    throw;\n}","preventionTips":["Always declare middleware entry points as `async ValueTask` or returning Task/ValueTask.","Never write void middleware; return Task.CompletedTask when no async work exists.","Rely on the interface (IMiddleware) so the return type is compiler-checked.","Avoid custom awaitable return types; the check accepts only Task/ValueTask."],"tags":["mediator","middleware","reflection","async"],"backgroundTag":"type-mismatch","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}