{"record":{"id":"bd876b021f08852f","repo":"quarkusio/quarkus","slug":"onclose-callback-must-return-unit-or-uni-void","errorCode":null,"errorMessage":"@OnClose callback must return Unit or Uni<Void>: ","messagePattern":"@OnClose callback must return Unit or Uni<Void>: ","errorType":"validation","errorClass":"WebSocketServerException","httpStatus":null,"severity":"error","filePath":"extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/WebSocketProcessor.java","lineNumber":1125,"sourceCode":"        } else {\n            if (!callback.isReturnTypeVoid() && !isUniVoid(callback.returnType())) {\n                throw new WebSocketServerException(\n                        \"@OnPongMessage callback must return void or Uni<Void>: \" + callback.asString());\n            }\n        }\n        Type messageType = callback.argumentType(MessageCallbackArgument::isMessage);\n        if (messageType == null || !messageType.name().equals(WebSocketDotNames.BUFFER)) {\n            throw new WebSocketServerException(\n                    \"@OnPongMessage callback must accept exactly one message parameter of type io.vertx.core.buffer.Buffer: \"\n                            + callback.asString());\n        }\n    }\n\n    private void validateOnClose(Callback callback) {\n        if (KotlinUtils.isKotlinMethod(callback.method)) {\n            if (!callback.isReturnTypeVoid() && !isUniVoid(callback.returnType())\n                    && !callback.isKotlinSuspendFunctionReturningUnit()) {\n                throw new WebSocketServerException(\n                        \"@OnClose callback must return Unit or Uni<Void>: \" + callback.asString());\n            }\n        } else {\n            if (!callback.isReturnTypeVoid() && !isUniVoid(callback.returnType())) {\n                throw new WebSocketServerException(\n                        \"@OnClose callback must return void or Uni<Void>: \" + callback.asString());\n            }\n        }\n    }\n\n    /**\n     * The generated endpoint class looks like:\n     *\n     * <pre>\n     * public class Echo_WebSocketEndpoint extends WebSocketEndpointBase {\n     *\n     *     public WebSocket.ExecutionMode executionMode() {\n     *         return WebSocket.ExecutionMode.SERIAL;","sourceCodeStart":1107,"sourceCodeEnd":1143,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/WebSocketProcessor.java#L1107-L1143","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Make the Kotlin @OnClose function return Unit (either explicitly or by having no meaningful return)","If suspending, declare it as a suspend fun returning Unit","Alternatively return Uni<Void> from Mutiny for reactive close handling"],"exampleFix":"// before (Kotlin)\n@OnClose\nfun onClose(): String { return \"closed\" }\n\n// after (Kotlin)\n@OnClose\nfun onClose() { /* cleanup */ }","handlingStrategy":"validation","validationCode":"// Kotlin check\nif (m.isAnnotationPresent(OnClose::class.java)) {\n    require(m.returnType == Unit::class.java || isUniVoid(m)) { \"@OnClose must return Unit or Uni<Void>\" }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Write close handlers as `fun onClose()` or `suspend fun onClose()`","Avoid returning Job/Deferred from lifecycle callbacks","Use Uni<Void> for reactive cleanup"],"tags":["quarkus","websockets-next","kotlin","callback-signature"],"backgroundTag":"callback-return-type-mismatch","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-12T22:17:10.623Z"}