quarkusio/quarkus · error · WebSocketException
Multiple @OnError callbacks may not accept the same error pa
Error message
Multiple @OnError callbacks may not accept the same error parameter: %s - %s - %s
What it means
An endpoint may register multiple @OnError callbacks, but each may only handle a distinct error type. When two @OnError methods accept the same error parameter type, the build fails with a WebSocketException listing the endpoint class and both conflicting callbacks.
Source
Thrown at extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/WebSocketProcessor.java:1321
InvokerInfo invoker = invokerFactory.createInvoker(callback.bean, callback.method)
.withInvocationWrapper(CoroutineInvoker.class, "inNewCoroutine")
.build();
bc.yield(bc.new_(ConstructorDesc.of(invoker.getClassDesc())));
});
});
}
return null;
}
private static List<ErrorHandler> generateErrorHandlers(io.quarkus.gizmo2.creator.ClassCreator cc,
WebSocketEndpointBuildItem endpoint, GlobalErrorHandlersBuildItem globalErrorHandlers, IndexView index,
InvokerFactoryBuildItem invokerFactory) {
List<ErrorHandler> result = new ArrayList<>();
Map<DotName, Callback> found = new HashMap<>();
for (Callback callback : endpoint.onErrors) {
DotName errorTypeName = callback.argumentType(ErrorCallbackArgument::isError).name();
if (found.containsKey(errorTypeName)) {
throw new WebSocketException(String.format(
"Multiple @OnError callbacks may not accept the same error parameter: %s\n\t- %s\n\t- %s",
errorTypeName, callback.asString(), found.get(errorTypeName).asString()));
}
found.put(errorTypeName, callback);
FieldDesc invoker = generateInvokerFieldIfNeeded(cc, callback,
"Error_" + errorTypeName.withoutPackagePrefix(), invokerFactory);
result.add(new ErrorHandler(endpoint.bean, callback, invoker, throwableHierarchy(errorTypeName, index)));
}
List<GlobalErrorHandler> handlers = endpoint.isClient
? globalErrorHandlers.forClient()
: globalErrorHandlers.forServer();
for (GlobalErrorHandler handler : handlers) {
Callback callback = handler.callback;
DotName errorTypeName = callback.argumentType(ErrorCallbackArgument::isError).name();
// Endpoint callbacks take precedence over global handlers
if (!found.containsKey(errorTypeName)) {
FieldDesc invoker = generateInvokerFieldIfNeeded(cc, callback,
"Error_" + errorTypeName.withoutPackagePrefix(), invokerFactory);View on GitHub (pinned to e1c734241f)
Solutions
- Remove the duplicate @OnError method, keeping one handler per error type
- If handlers differ in behavior, narrow one to a more specific exception subclass
- Consolidate logic into a single @OnError method
Example fix
// before
class MyWs {
@OnError void onErr(Exception e) {}
@OnError void onErr2(Exception e) {}
}
// after
class MyWs {
@OnError void onErr(Exception e) { /* single handler */ }
} Defensive patterns
Strategy: validation
Validate before calling
var seen = new HashSet<Class<?>>();
for (Method m : MyWebSocket.class.getDeclaredMethods()) {
if (m.isAnnotationPresent(OnError.class)) {
var errTypes = Arrays.stream(m.getParameterTypes())
.filter(Throwable.class::isAssignableFrom).toList();
for (var t : errTypes) {
if (!seen.add(t)) throw new IllegalStateException("Duplicate @OnError for " + t);
}
}
} Prevention
- One @OnError per exception type per endpoint
- Search the endpoint class for existing @OnError before adding a new one
- Use exception subclasses to differentiate handlers
When it happens
Trigger: Two methods in one @WebSocket endpoint both annotated @OnError with the same error parameter type (e.g. both taking Exception or both taking a custom MyException).
Common situations: Copy-pasting an error handler and forgetting to remove the old one; two handlers added by different team members; merging branches that each added an @OnError for the same exception type.
Related errors
- @OnPongMessage callback must return void or Uni<Void>:
- @OnPongMessage callback must accept exactly one message para
- @OnClose callback must return void or Uni<Void>:
- @OnError callback must accept exactly one error parameter; f
- @%s callback may accept at most 1 message parameter; found %
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/fadeaae32327bf2f.
Report an issue: GitHub.