Diagnostics
Reference for all compile-time diagnostics emitted by the Mocha source generator, including causes, examples, and fixes for each warning and error.
Diagnostics
Mocha uses a Roslyn source generator to validate your message handlers, consumers, and sagas at compile time. When the generator detects a problem - a missing handler, a duplicate registration, an invalid type - it emits a diagnostic that appears as a compiler warning or error in your IDE and build output. You can fix these issues before your code ever runs.
Quick reference
| Code | Description | Severity |
|---|---|---|
| MO0001 | Missing handler for message type | Warning |
| MO0002 | Duplicate handler for message type | Error |
| MO0003 | Handler is abstract | Warning |
| MO0004 | Open generic message type cannot be dispatched | Info |
| MO0005 | Handler implements multiple mediator handler interfaces | Error |
| MO0011 | Duplicate handler for request type | Error |
| MO0012 | Open generic messaging handler cannot be auto-registered | Info |
| MO0013 | Messaging handler is abstract | Warning |
| MO0014 | Saga must have a public parameterless constructor | Error |
Mediator diagnostics
These diagnostics apply to the in-process mediator - commands, queries, and notifications dispatched within a single process.
MO0001
Missing handler for message type
| Severity | Warning |
| Message | Message type '{0}' has no registered handler |
Cause
A command or query type is declared but no corresponding handler implementation exists. The mediator requires exactly one handler for each command and query type. This diagnostic does not apply to notifications, which can have zero handlers.
Example
using Mocha.Mediator;
// Command with no handler - triggers MO0001public record PlaceOrder(Guid OrderId, decimal Total) : ICommand;
Fix
Implement a handler for the message type.
using Mocha.Mediator;
public record PlaceOrder(Guid OrderId, decimal Total) : ICommand;
public class PlaceOrderHandler : ICommandHandler<PlaceOrder>{ public ValueTask HandleAsync( PlaceOrder command, CancellationToken cancellationToken) { // process the order return ValueTask.CompletedTask; }}
MO0002
Duplicate handler for message type
| Severity | Error |
| Message | Message type '{0}' has multiple handlers: {1} |
Cause
A command or query type has more than one handler implementation. Commands and queries require exactly one handler - the mediator cannot decide which one to call. This diagnostic does not apply to notifications, which support multiple handlers by design.
Example
using Mocha.Mediator;
public record PlaceOrder(Guid OrderId, decimal Total) : ICommand;
// Two handlers for the same command - triggers MO0002public class PlaceOrderHandler : ICommandHandler<PlaceOrder>{ public ValueTask HandleAsync(PlaceOrder command, CancellationToken ct) => ValueTask.CompletedTask;}
public class DuplicateOrderHandler : ICommandHandler<PlaceOrder>{ public ValueTask HandleAsync(PlaceOrder command, CancellationToken ct) => ValueTask.CompletedTask;}
Fix
Remove all but one handler. If you need multiple side effects for the same action, consider publishing a notification from the single handler and reacting to it with separate notification handlers.
using Mocha.Mediator;
public record PlaceOrder(Guid OrderId, decimal Total) : ICommand;
public class PlaceOrderHandler : ICommandHandler<PlaceOrder>{ public ValueTask HandleAsync(PlaceOrder command, CancellationToken ct) => ValueTask.CompletedTask;}
MO0003
Handler is abstract
| Severity | Warning |
| Message | Handler '{0}' is abstract and will not be registered |
Cause
A class implements a handler interface (ICommandHandler, IQueryHandler, or INotificationHandler) but is declared abstract. The source generator skips abstract types because they cannot be instantiated.
Example
using Mocha.Mediator;
public record GetOrderTotal(Guid OrderId) : IQuery<decimal>;
// Abstract handler - triggers MO0003public abstract class GetOrderTotalHandler : IQueryHandler<GetOrderTotal, decimal>{ public abstract ValueTask<decimal> HandleAsync( GetOrderTotal query, CancellationToken cancellationToken);}
Fix
Make the handler concrete. If you want shared base logic, move it to a base class that does not implement the handler interface, and have the concrete handler extend it.
using Mocha.Mediator;
public record GetOrderTotal(Guid OrderId) : IQuery<decimal>;
public class GetOrderTotalHandler : IQueryHandler<GetOrderTotal, decimal>{ public ValueTask<decimal> HandleAsync( GetOrderTotal query, CancellationToken cancellationToken) { return ValueTask.FromResult(99.99m); }}
MO0004
Open generic message type cannot be dispatched
| Severity | Info |
| Message | Message type '{0}' is an open generic and cannot be dispatched at runtime |
Cause
A command or query type has unbound type parameters. The mediator dispatches concrete types at runtime and cannot resolve an open generic like MyCommand<T>.
Example
using Mocha.Mediator;
// Open generic command - triggers MO0004public record ProcessItem<T>(T Item) : ICommand;
Fix
Use concrete message types instead.
using Mocha.Mediator;
public record ProcessOrder(Guid OrderId) : ICommand;public record ProcessPayment(decimal Amount) : ICommand;
MO0005
Handler implements multiple mediator handler interfaces
| Severity | Error |
| Message | Handler '{0}' must implement exactly one mediator handler interface |
Cause
A single class implements more than one of ICommandHandler, IQueryHandler, or INotificationHandler. Each handler class must implement exactly one mediator handler interface so the generator can produce unambiguous registrations.
Example
using Mocha.Mediator;
public record PlaceOrder(Guid OrderId) : ICommand;public record GetOrder(Guid OrderId) : IQuery<Order>;
// Implements both command and query handler - triggers MO0005public class OrderHandler : ICommandHandler<PlaceOrder>, IQueryHandler<GetOrder, Order>{ public ValueTask HandleAsync(PlaceOrder command, CancellationToken ct) => ValueTask.CompletedTask;
public ValueTask<Order> HandleAsync(GetOrder query, CancellationToken ct) => ValueTask.FromResult(new Order());}
Fix
Split into separate handler classes, one per interface.
using Mocha.Mediator;
public record PlaceOrder(Guid OrderId) : ICommand;public record GetOrder(Guid OrderId) : IQuery<Order>;
public class PlaceOrderHandler : ICommandHandler<PlaceOrder>{ public ValueTask HandleAsync(PlaceOrder command, CancellationToken ct) => ValueTask.CompletedTask;}
public class GetOrderHandler : IQueryHandler<GetOrder, Order>{ public ValueTask<Order> HandleAsync(GetOrder query, CancellationToken ct) => ValueTask.FromResult(new Order());}
Messaging diagnostics
These diagnostics apply to the message bus - event handlers, request handlers, batch handlers, consumers, and sagas that communicate across service boundaries.
MO0011
Duplicate handler for request type
| Severity | Error |
| Message | Request type '{0}' has multiple handlers: {1} |
Cause
A request type (used with SendAsync or RequestAsync) has more than one handler implementation. Request types require exactly one handler - the bus cannot route to multiple targets.
Example
using Mocha;
public record ProcessPayment(decimal Amount);
// Two handlers for the same request type - triggers MO0011public class PaymentHandlerA : IEventRequestHandler<ProcessPayment>{ public ValueTask HandleAsync( ProcessPayment request, CancellationToken ct) => ValueTask.CompletedTask;}
public class PaymentHandlerB : IEventRequestHandler<ProcessPayment>{ public ValueTask HandleAsync( ProcessPayment request, CancellationToken ct) => ValueTask.CompletedTask;}
Fix
Keep one handler per request type.
using Mocha;
public record ProcessPayment(decimal Amount);
public class ProcessPaymentHandler : IEventRequestHandler<ProcessPayment>{ public ValueTask HandleAsync( ProcessPayment request, CancellationToken ct) => ValueTask.CompletedTask;}
MO0012
Open generic messaging handler cannot be auto-registered
| Severity | Info |
| Message | Handler '{0}' is an open generic and cannot be auto-registered |
Cause
A messaging handler (IEventHandler<T>, IEventRequestHandler<T>, IBatchEventHandler<T>, or IConsumer<T>) has unbound type parameters. The source generator cannot produce registration code for open generic types.
Example
using Mocha;
// Open generic handler - triggers MO0012public class GenericEventHandler<T> : IEventHandler<T>{ public ValueTask HandleAsync( T message, CancellationToken ct) => ValueTask.CompletedTask;}
Fix
Make the handler concrete. If you need to handle multiple event types with shared logic, create a concrete handler for each type and extract the shared logic into a base class or shared service.
If you need to register an open generic handler, register it manually through DI instead of relying on auto-registration.
using Mocha;
public record OrderPlaced(Guid OrderId);
public class OrderPlacedHandler : IEventHandler<OrderPlaced>{ public ValueTask HandleAsync( OrderPlaced message, CancellationToken ct) => ValueTask.CompletedTask;}
MO0013
Messaging handler is abstract
| Severity | Warning |
| Message | Handler '{0}' is abstract and will not be registered |
Cause
A class implements a messaging handler interface but is declared abstract. The source generator skips abstract types because they cannot be instantiated.
Example
using Mocha;
public record OrderPlaced(Guid OrderId);
// Abstract handler - triggers MO0013public abstract class OrderEventHandler : IEventHandler<OrderPlaced>{ public abstract ValueTask HandleAsync( OrderPlaced message, CancellationToken ct);}
Fix
Make the handler concrete. If you need shared base logic, move it to a base class that does not implement the handler interface.
using Mocha;
public record OrderPlaced(Guid OrderId);
public class OrderPlacedHandler : IEventHandler<OrderPlaced>{ public ValueTask HandleAsync( OrderPlaced message, CancellationToken ct) => ValueTask.CompletedTask;}
MO0014
Saga must have a public parameterless constructor
| Severity | Error |
| Message | Saga '{0}' must have a public parameterless constructor |
Cause
A Saga<TState> subclass does not have a public parameterless constructor. The saga infrastructure requires this constructor to instantiate the saga type. This is enforced by the new() constraint on the AddSaga<T> registration method.
Example
using Mocha.Sagas;
public class RefundSagaState : SagaStateBase{ public Guid OrderId { get; set; }}
// Constructor requires a parameter - triggers MO0014public class RefundSaga : Saga<RefundSagaState>{ private readonly ILogger _logger;
public RefundSaga(ILogger logger) { _logger = logger; }}
Fix
Add a public parameterless constructor. Sagas are configured through their state machine definition, not through constructor injection. If you need dependencies, access them through the saga's built-in service resolution.
using Mocha.Sagas;
public class RefundSagaState : SagaStateBase{ public Guid OrderId { get; set; }}
public class RefundSaga : Saga<RefundSagaState>{ public RefundSaga() { }}