quarkusio/quarkus · error · WebSocketServerException
@OnPingMessage callback must return Unit or Uni<Void>:
Error message
@OnPingMessage callback must return Unit or Uni<Void>:
What it means
An @OnPingMessage callback in Kotlin must return Unit, Uni<Void>, or be a suspend function returning Unit. Any other return type is rejected at build time so ping handling stays fire-and-forget or reactive-void. The message includes the offending callback's string form.
Source
Thrown at extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/WebSocketProcessor.java:1083
throw new WebSocketServerException("Enclosing class not found in index: " + enclosingClass);
}
AnnotationInstance webSocketAnnotation = enclosingClass.annotation(WebSocketDotNames.WEB_SOCKET);
if (webSocketAnnotation != null) {
String path = getPath(webSocketAnnotation.value("path").asString());
if (enclosingClass.nestingType() == NestingType.INNER) {
return mergePath(getPathPrefix(index, enclosingClass.enclosingClass()), path);
} else {
return path.endsWith("/") ? path.substring(path.length() - 1) : path;
}
}
return "";
}
private void validateOnPingMessage(Callback callback) {
if (KotlinUtils.isKotlinMethod(callback.method)) {
if (!callback.isReturnTypeVoid() && !isUniVoid(callback.returnType())
&& !callback.isKotlinSuspendFunctionReturningUnit()) {
throw new WebSocketServerException(
"@OnPingMessage callback must return Unit or Uni<Void>: " + callback.asString());
}
} else {
if (!callback.isReturnTypeVoid() && !isUniVoid(callback.returnType())) {
throw new WebSocketServerException(
"@OnPingMessage callback must return void or Uni<Void>: " + callback.asString());
}
}
Type messageType = callback.argumentType(MessageCallbackArgument::isMessage);
if (messageType == null || !messageType.name().equals(WebSocketDotNames.BUFFER)) {
throw new WebSocketServerException(
"@OnPingMessage callback must accept exactly one message parameter of type io.vertx.core.buffer.Buffer: "
+ callback.asString());
}
}
private void validateOnPongMessage(Callback callback) {
if (KotlinUtils.isKotlinMethod(callback.method)) {View on GitHub (pinned to e1c734241f)
Solutions
- Change the callback return type to Unit (or make it suspend returning Unit).
- Alternatively return io.smallrye.mutiny.Uni<Void>.
- Return the computed value via a different mechanism (e.g. send a message from within the handler) instead of returning it from the ping callback.
Example fix
// before
@OnPingMessage
fun onPing(buffer: Buffer): String { return "pong" }
// after
@OnPingMessage
fun onPing(buffer: Buffer) { /* handle */ } Defensive patterns
Strategy: validation
Validate before calling
// Kotlin pre-check: @OnPingMessage must return Unit/Uni<Void> or suspend Unit
fun checkPingCallback(m: KFunction<*>) {
val ret = m.returnType.classifier
check(ret == Unit::class || ret == Uni::class)
{ "@OnPingMessage must return Unit or Uni<Void>: ${m.name}" }
} Prevention
- Declare ping/pong handlers without an explicit return type in Kotlin
- Use suspend only when needed; Unit return is simplest
- Do not return echo values from ping callbacks
- Lint for callback signatures in CI
When it happens
Trigger: A Kotlin method annotated @OnPingMessage returns a non-Unit, non-Uni<Void> type (e.g. String, Int, Uni<String>) and is neither plain void nor a suspend function returning Unit.
Common situations: Returning the pong/echo value from the ping handler; forgetting that Kotlin Unit maps through suspend wrappers; copying Java signatures into Kotlin without adjusting the return type.
Related errors
- @OnPongMessage callback must return Unit or Uni<Void>:
- @OnPingMessage callback must return void or Uni<Void>:
- @OnPingMessage callback must accept exactly one message para
- Something went wrong during parameter type resolution - expe
- Unable to inject @%s callback parameter '%s' declared on %s:
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/fade16544820c78a.
Report an issue: GitHub.