quarkusio/quarkus · error · WebSocketServerException
@OnClose callback must return void or Uni<Void>:
Error message
@OnClose callback must return void or Uni<Void>:
What it means
For non-Kotlin @OnClose callbacks, the method must return void or Uni<Void>. Close callbacks are invoked on connection close and their return value is discarded, so any other return type fails the build-time signature validation with WebSocketServerException.
Source
Thrown at extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/WebSocketProcessor.java:1130
}
Type messageType = callback.argumentType(MessageCallbackArgument::isMessage);
if (messageType == null || !messageType.name().equals(WebSocketDotNames.BUFFER)) {
throw new WebSocketServerException(
"@OnPongMessage callback must accept exactly one message parameter of type io.vertx.core.buffer.Buffer: "
+ callback.asString());
}
}
private void validateOnClose(Callback callback) {
if (KotlinUtils.isKotlinMethod(callback.method)) {
if (!callback.isReturnTypeVoid() && !isUniVoid(callback.returnType())
&& !callback.isKotlinSuspendFunctionReturningUnit()) {
throw new WebSocketServerException(
"@OnClose callback must return Unit or Uni<Void>: " + callback.asString());
}
} else {
if (!callback.isReturnTypeVoid() && !isUniVoid(callback.returnType())) {
throw new WebSocketServerException(
"@OnClose callback must return void or Uni<Void>: " + callback.asString());
}
}
}
/**
* The generated endpoint class looks like:
*
* <pre>
* public class Echo_WebSocketEndpoint extends WebSocketEndpointBase {
*
* public WebSocket.ExecutionMode executionMode() {
* return WebSocket.ExecutionMode.SERIAL;
* }
*
* public Echo_WebSocketEndpoint(WebSocketConnection connection, Codecs codecs,
* WebSocketRuntimeConfig config, ContextSupport contextSupport, SecuritySupport securitySupport) {
* super(connection, codecs, config, contextSupport, securitySupport);View on GitHub (pinned to e1c734241f)
Solutions
- Change the return type to void
- For async cleanup, return Uni<Void> and complete with null
- If you need CompletionStage semantics, wrap it: Uni.createFrom().completionStage(...)
Example fix
// before
@OnClose
CompletionStage<Void> onClose() { return done; }
// after
@OnClose
Uni<Void> onClose() { return Uni.createFrom().completionStage(() -> done); } Defensive patterns
Strategy: validation
Validate before calling
for (Method m : MyWebSocket.class.getDeclaredMethods()) {
if (m.isAnnotationPresent(OnClose.class)) {
boolean ok = m.getReturnType().equals(void.class) || Uni.class.isAssignableFrom(m.getReturnType());
if (!ok) throw new IllegalStateException(m + " must return void or Uni<Void>");
}
} Prevention
- Default to void for @OnClose
- Remember CompletionStage is not accepted — use Mutiny Uni<Void>
- Run quarkus:dev to catch signature errors immediately
When it happens
Trigger: A Java @OnClose method returning e.g. String, int, CompletionStage<Void>, io.smallrye.mutiny.Uni<String>, or any POJO.
Common situations: Returning CompletionStage instead of Mutiny Uni (not accepted); returning a status/result object; copy-pasting from @OnTextMessage handlers that return message replies.
Related errors
- @OnPongMessage callback must return void or Uni<Void>:
- @OnPongMessage callback must accept exactly one message para
- @OnError callback must accept exactly one error parameter; f
- @%s callback may accept at most 1 message parameter; found %
- @%s callback must not accept a message parameter; found %s:
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/105b095c3a3771c2.
Report an issue: GitHub.