Summary
When the duplicate-TypeName disambiguation in HandlerGraph fires for an array message type (T[]), it builds a generated class name that still contains the [] characters. That is not a valid C# identifier, so the generated handler assembly fails to compile and the whole application dies at runtime with Compilation failures!.
Array message types are not exotic: opts.BatchMessagesOf<T>() makes T[] the handled message type, so any batched message can hit this.
Repro
Full standalone repro (WolverineFx 6.17.2 + WolverineFx.RuntimeCompilation, .NET 10):
using Microsoft.Extensions.Hosting;
using Wolverine;
var builder = Host.CreateApplicationBuilder(args);
builder.UseWolverine(opts =>
{
opts.MultipleHandlerBehavior = MultipleHandlerBehavior.Separated;
opts.BatchMessagesOf<ItemDeleted>(batching => batching.BatchSize = 100);
});
var host = builder.Build();
await host.StartAsync();
await host.MessageBus().PublishAsync(new ItemDeleted(1)); // boom
await Task.Delay(2000);
await host.StopAsync();
public record ItemCreated(int Id);
public record ItemDeleted(int Id);
// This class handles TWO message types, one of which is batched (=> ItemDeleted[])
public static class TelemetryHandler
{
public static void Handle(ItemCreated message) { }
public static void Handle(ItemDeleted[] messages) { }
}
// A second handler class per message type, so MultipleHandlerBehavior.Separated
// actually splits the chains
public static class AuditHandler
{
public static void Handle(ItemCreated message) { }
}
public static class PublisherHandler
{
public static void Handle(ItemDeleted[] messages) { }
}
Result:
System.InvalidOperationException: Compilation failures!
CS1514: { expected
CS1513: } expected
CS1001: Identifier expected
CS1013: Invalid number
CS0116: A namespace cannot directly contain members such as fields, methods or statements
CS1022: Type or namespace definition, or end-of-file expected
dotnet run -- codegen write shows the culprit directly — note the file/class name:
Internal/Generated/WolverineHandlers/ItemCreated1902602641_TelemetryHandlerHandler550305999.cs
Internal/Generated/WolverineHandlers/ItemDeleted[]1177234954_TelemetryHandlerHandler550305999.cs <-- invalid identifier
Internal/Generated/WolverineHandlers/AuditHandlerHandler1032311623.cs
Internal/Generated/WolverineHandlers/PublisherHandlerHandler1633587262.cs
which produces:
public sealed class ItemDeleted[]1177234954_TelemetryHandlerHandler550305999 : Wolverine.Runtime.Handlers.MessageHandler
Conditions
The chains are only renamed when two of them collide on TypeName, so all of these are needed:
MultipleHandlerBehavior.Separated (one chain per handler class),
- a handler class that handles two or more message types, each of which has another handler class (otherwise the chains keep their message-type-based names and never collide),
- at least one of those message types is an array — in practice, a type registered with
BatchMessagesOf<T>().
Root cause
Both HandlerChain constructors sanitize the brackets:
https://github.com/JasperFx/wolverine/blob/main/src/Wolverine/Runtime/Handlers/HandlerChain.cs#L69
TypeName = messageType.ToSuffixedTypeName(HandlerSuffix).Replace("[]", "Array");
https://github.com/JasperFx/wolverine/blob/main/src/Wolverine/Runtime/Handlers/HandlerChain.cs#L85
TypeName = call.HandlerType.ToSuffixedTypeName(HandlerSuffix).Replace("[]", "Array");
but the duplicate-name disambiguation added for #2004 does not:
https://github.com/JasperFx/wolverine/blob/main/src/Wolverine/Runtime/Handlers/HandlerGraph.cs#L389
chain.TypeName =
$"{chain.MessageType.ToSuffixedTypeName("")}_{chain.HandlerCalls().First().HandlerType.ToSuffixedTypeName("Handler")}";
chain.MessageType.ToSuffixedTypeName("") yields ItemDeleted[]1177234954 for an array message type, and the brackets flow straight into the generated class name.
Suggested fix
Apply the same sanitization in the disambiguation path:
chain.TypeName =
$"{chain.MessageType.ToSuffixedTypeName("")}_{chain.HandlerCalls().First().HandlerType.ToSuffixedTypeName("Handler")}"
.Replace("[]", "Array");
Workaround
Make sure no handler class handles both an array message type and another message type that would separate — e.g. move the T[] handler into its own class, or keep a single handler class for the batched type.
Summary
When the duplicate-
TypeNamedisambiguation inHandlerGraphfires for an array message type (T[]), it builds a generated class name that still contains the[]characters. That is not a valid C# identifier, so the generated handler assembly fails to compile and the whole application dies at runtime withCompilation failures!.Array message types are not exotic:
opts.BatchMessagesOf<T>()makesT[]the handled message type, so any batched message can hit this.Repro
Full standalone repro (
WolverineFx6.17.2 +WolverineFx.RuntimeCompilation, .NET 10):Result:
dotnet run -- codegen writeshows the culprit directly — note the file/class name:which produces:
Conditions
The chains are only renamed when two of them collide on
TypeName, so all of these are needed:MultipleHandlerBehavior.Separated(one chain per handler class),BatchMessagesOf<T>().Root cause
Both
HandlerChainconstructors sanitize the brackets:https://github.com/JasperFx/wolverine/blob/main/src/Wolverine/Runtime/Handlers/HandlerChain.cs#L69
https://github.com/JasperFx/wolverine/blob/main/src/Wolverine/Runtime/Handlers/HandlerChain.cs#L85
but the duplicate-name disambiguation added for #2004 does not:
https://github.com/JasperFx/wolverine/blob/main/src/Wolverine/Runtime/Handlers/HandlerGraph.cs#L389
chain.MessageType.ToSuffixedTypeName("")yieldsItemDeleted[]1177234954for an array message type, and the brackets flow straight into the generated class name.Suggested fix
Apply the same sanitization in the disambiguation path:
Workaround
Make sure no handler class handles both an array message type and another message type that would separate — e.g. move the
T[]handler into its own class, or keep a single handler class for the batched type.