There's a specific ASP.NET Core error that has cost me and my team more time than its difficulty deserves:
The delegate 'RequestDelegate' does not take 2 arguments
You wrote a Minimal API endpoint, it looks exactly like the twenty others in the file, and the compiler is telling you something that appears to have nothing to do with your code. The error names a type you didn't write, and a parameter count that isn't obviously wrong.
It's a good example of a broader skill worth talking about: what to do when a compiler error is accurate but unhelpful.
What's Actually Happening
The Minimal API mapping methods are overloaded. One set takes a Delegate — which accepts almost any lambda shape and does parameter binding for you, inferring from route templates, query strings, the DI container, and the body. Another takes a RequestDelegate, which is specifically Func<HttpContext, Task>: one parameter, one return type, no binding.
When your lambda doesn't match anything the flexible overload can accept, overload resolution falls through to RequestDelegate — and then reports the error against that signature.
So the message is telling the truth about the overload the compiler ended up on, which is not the overload you meant, which is why it reads as nonsense. It's a correct answer to a question you didn't ask.
The underlying cause is almost always one of:
- A parameter type the binder can't resolve, often a service you forgot to register, or one whose binding source is ambiguous.
- A missing attribute where inference isn't possible — the binder can't tell whether something comes from the body or the query.
- A lambda returning something unexpected, frequently
async voidinstead ofasync Task. - A tuple or complex type in the signature that binding has no rule for.
Narrowing It Down
The practical technique, which generalises well beyond this error:
Reduce until it compiles. Strip the endpoint to a lambda taking nothing and returning a constant. It will compile. Add parameters back one at a time. The one that breaks it is your answer, and this takes about ninety seconds — reliably faster than staring at the original.
Replace the lambda with a named method. Give the method an explicit signature and the compiler's error moves from overload resolution to the actual parameter, which is usually a much clearer message. This is worth doing for any complex endpoint anyway.
Check registration before syntax. When the offending parameter is a service, the problem is usually the DI container, not the endpoint.
Be explicit. [FromBody], [FromQuery], [FromServices] cost nothing and remove the inference the compiler was failing at. On any endpoint that isn't trivially simple, I'd rather read explicit attributes than reconstruct binding rules from memory.
The General Skill
The reason I think this is worth a post isn't the specific error. It's that compiler errors are accurate about the compiler's state and often unhelpful about your intent, and the gap between those two is where debugging time goes.
The productive question isn't "what does this message mean?" It's "what would have to be true for the compiler to say this?" Here, that's: the compiler chose an overload you didn't intend, which means your lambda didn't match the intended one, which means something in your signature isn't bindable.
That reframing turns a confusing message into a search with one obvious next step. The same move works on most bewildering type errors — generic inference failures, ambiguous extension methods, nullable mismatches deep in a LINQ chain. The error is a symptom of a decision the compiler made; find the decision.
And Report Them
One last thing. When an error message is genuinely misleading, it's worth a few minutes to file an issue on the relevant repository.
Diagnostic quality is a real feature, and it improves when maintainers know which messages send people down the wrong path. Several of the sharpest edges in the .NET toolchain have been smoothed because enough people reported that a message, while technically correct, taught nobody anything.
The alternative is that every team rediscovers the same confusion independently and writes the same blog post about it — which, I'm aware, is roughly what I've just done.



