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

CodeDescriptionSeverity
MO0001Missing handler for message typeWarning
MO0002Duplicate handler for message typeError
MO0003Handler is abstractWarning
MO0004Open generic message type cannot be dispatchedInfo
MO0005Handler implements multiple mediator handler interfacesError
MO0011Duplicate handler for request typeError
MO0012Open generic messaging handler cannot be auto-registeredInfo
MO0013Messaging handler is abstractWarning
MO0014Saga must have a public parameterless constructorError

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

SeverityWarning
MessageMessage 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

C#
using Mocha.Mediator;
// Command with no handler - triggers MO0001
public record PlaceOrder(Guid OrderId, decimal Total) : ICommand;

Fix

Implement a handler for the message type.

C#
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

SeverityError
MessageMessage 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

C#
using Mocha.Mediator;
public record PlaceOrder(Guid OrderId, decimal Total) : ICommand;
// Two handlers for the same command - triggers MO0002
public 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.

C#
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

SeverityWarning
MessageHandler '{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

C#
using Mocha.Mediator;
public record GetOrderTotal(Guid OrderId) : IQuery<decimal>;
// Abstract handler - triggers MO0003
public 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.

C#
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

SeverityInfo
MessageMessage 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

C#
using Mocha.Mediator;
// Open generic command - triggers MO0004
public record ProcessItem<T>(T Item) : ICommand;

Fix

Use concrete message types instead.

C#
using Mocha.Mediator;
public record ProcessOrder(Guid OrderId) : ICommand;
public record ProcessPayment(decimal Amount) : ICommand;

MO0005

Handler implements multiple mediator handler interfaces

SeverityError
MessageHandler '{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

C#
using Mocha.Mediator;
public record PlaceOrder(Guid OrderId) : ICommand;
public record GetOrder(Guid OrderId) : IQuery<Order>;
// Implements both command and query handler - triggers MO0005
public 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.

C#
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

SeverityError
MessageRequest 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

C#
using Mocha;
public record ProcessPayment(decimal Amount);
// Two handlers for the same request type - triggers MO0011
public 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.

C#
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

SeverityInfo
MessageHandler '{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

C#
using Mocha;
// Open generic handler - triggers MO0012
public 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.

C#
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

SeverityWarning
MessageHandler '{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

C#
using Mocha;
public record OrderPlaced(Guid OrderId);
// Abstract handler - triggers MO0013
public 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.

C#
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

SeverityError
MessageSaga '{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

C#
using Mocha.Sagas;
public class RefundSagaState : SagaStateBase
{
public Guid OrderId { get; set; }
}
// Constructor requires a parameter - triggers MO0014
public 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.

C#
using Mocha.Sagas;
public class RefundSagaState : SagaStateBase
{
public Guid OrderId { get; set; }
}
public class RefundSaga : Saga<RefundSagaState>
{
public RefundSaga()
{
}
}
Last updated on April 13, 2026 by Michael Staib