quarkusio/quarkus · error · WebSocketServerException

@OnClose callback must return Unit or Uni<Void>:

Error message

@OnClose callback must return Unit or Uni<Void>: 

What it means

For Kotlin @OnClose callbacks, the method must return Unit (including suspend functions returning Unit) or Uni<Void>. A close callback performs shutdown side effects and must not return a value. When isKotlinMethod detects a Kotlin method whose return type is none of these, the build throws WebSocketServerException.

Source

Thrown at extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/WebSocketProcessor.java:1125

        } else {
            if (!callback.isReturnTypeVoid() && !isUniVoid(callback.returnType())) {
                throw new WebSocketServerException(
                        "@OnPongMessage 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(
                    "@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;

View on GitHub (pinned to e1c734241f)

Solutions

  1. Make the Kotlin @OnClose function return Unit (either explicitly or by having no meaningful return)
  2. If suspending, declare it as a suspend fun returning Unit
  3. Alternatively return Uni<Void> from Mutiny for reactive close handling

Example fix

// before (Kotlin)
@OnClose
fun onClose(): String { return "closed" }

// after (Kotlin)
@OnClose
fun onClose() { /* cleanup */ }
Defensive patterns

Strategy: validation

Validate before calling

// Kotlin check
if (m.isAnnotationPresent(OnClose::class.java)) {
    require(m.returnType == Unit::class.java || isUniVoid(m)) { "@OnClose must return Unit or Uni<Void>" }
}

Prevention

When it happens

Trigger: A Kotlin method annotated @OnClose that returns a non-Unit type such as String, Boolean, or Job, and is not a suspend function returning Unit nor a Uni<Void> returner.

Common situations: Kotlin developer forgets the trailing Unit return convention when converting a Java void callback; returning a coroutine Job from the close handler; refactoring an @OnClose method to return a result.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/bd876b021f08852f. Report an issue: GitHub.