ValidateMediatorHandlers() is a source-generated extension method on IServiceProvider that detects misconfigured handlers at startup — before the first request is processed. It resolves every registered handler, pipeline chain, and notification handler from DI, and throws an AggregateException containing everything it found.
It reports two kinds of problem: a registration that cannot be resolved at all, and a registration that resolves perfectly well but will not behave the way it was written. The second kind is the one nothing else catches — see Registration-order problems.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMediator(b =>
{
b.AddOpenBehavior(typeof(LoggingBehavior<,>));
b.AddRequestPreProcessor<ValidationPreProcessor>();
});
var app = builder.Build();
// Fail fast — detect missing handlers, broken dependencies, incomplete pipelines
app.Services.ValidateMediatorHandlers();
app.Run();
This works with both registration approaches — AddMediator(configure) and the step-by-step RegisterMediatorHandlers() + PrecompilePipelines(). The validation method operates on the built IServiceProvider, not on the registration API.
| Handler type | Validation |
|---|---|
| Request handlers | GetRequiredService<IRequestHandler<T,R>>() + GetService<PipelineChainHandler<T,R>>() |
| Self-handling requests | GetRequiredService<IRequestHandler<T,R>>() (validates the generated adapter and its DI dependencies) |
| Notification handlers | GetServices<INotificationHandler<T>>() + materialization of all implementations |
| Stream handlers | GetRequiredService<IStreamRequestHandler<T,R>>() + GetService<StreamPipelineChainHandler<T,R>>() |
| Late-registered components | GetServices<LatePipelineComponentReport>() — names every pipeline component a second AddMediator(configure) registered after the chains were frozen |
These resolve without error and would run misconfigured. DSOFT010 catches them at compile time when
the registration and the scan sit in the same method; everything else — another method, another
assembly, another module — reaches you here.
| Reported | Why it matters |
|---|---|
A Transient pipeline behavior whose chain is not Transient |
The chain is constructed once and holds that behavior, so it is shared for the life of the chain instead of built per dispatch — the opposite of what Transient asked for. |
An IMediatorDispatchObserver with no chain anywhere |
The observer runs inside the pipeline chain, so with no chain built it never runs at all. |
Components registered by a second AddMediator(configure) |
The overload runs your lambda on every call, but the scan behind it returns early once it has run — so those components get no chain rebuilt around them. They either never run, or a Singleton chain from the first scan captures them and the container refuses to build under ValidateScopes. |
The fix is the same in every case: register the whole pipeline before the scan, in the first
AddMediator(configure).
System.AggregateException: One or more mediator handlers failed validation.
---> System.InvalidOperationException: No service for type 'IRequestHandler<CreateUser, Guid>'
---> System.InvalidOperationException: Unable to resolve service for type 'IUserRepository'
while attempting to activate 'GetUserHandler'
Recommendation: Call
ValidateMediatorHandlers()in development and staging environments. In production, consider gating it behind a configuration flag to avoid the startup cost of resolving every handler.
The source generator emits ValidateMediatorHandlers() at compile time with explicit GetRequiredService<T>() calls for every handler it discovered. This means:
AggregateException, so you see every problem in one passAddMediator(configure) vs step-by-step registration