{"record":{"id":"97f88d0234271ceb","repo":"quarkusio/quarkus","slug":"onerror-callback-must-accept-exactly-one-error-pa","errorCode":null,"errorMessage":"@OnError callback must accept exactly one error parameter; found %s: %s","messagePattern":"@OnError callback must accept exactly one error parameter; found (.+?): (.+?)","errorType":"validation","errorClass":"WebSocketException","httpStatus":null,"severity":"error","filePath":"extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/WebSocketProcessor.java","lineNumber":1859,"sourceCode":"                    throw new WebSocketException(\"@OnError callback on @WebSocketClient must not accept WebSocketConnection: \"\n                            + method.declaringClass() + \".\" + method.name() + \"()\");\n                }\n            } else if (method.parameterTypes().stream().map(Type::name)\n                    .anyMatch(WebSocketDotNames.WEB_SOCKET_CLIENT_CONNECTION::equals)) {\n                target = Target.CLIENT;\n                if (expectedTarget == Target.SERVER) {\n                    throw new WebSocketException(\"@OnError callback on @WebSocket must not accept WebSocketClientConnection: \"\n                            + method.declaringClass() + \".\" + method.name() + \"()\");\n                }\n            } else {\n                target = Target.UNDEFINED;\n            }\n            Callback callback = new Callback(target, annotation, bean, method,\n                    executionModel(method, transformedAnnotations), callbackArguments, transformedAnnotations,\n                    endpointPath, index);\n            long errorArguments = callback.arguments.stream().filter(ca -> ca instanceof ErrorCallbackArgument).count();\n            if (errorArguments != 1) {\n                throw new WebSocketException(\n                        String.format(\"@OnError callback must accept exactly one error parameter; found %s: %s\",\n                                errorArguments, callback.asString()));\n            }\n            errorHandlers.add(callback);\n        }\n        return errorHandlers;\n    }\n\n    private static List<AnnotationInstance> findCallbackAnnotations(IndexView index, ClassInfo beanClass,\n            DotName annotationName) {\n        ClassInfo clazz = beanClass;\n        List<AnnotationInstance> annotations = new ArrayList<>();\n        while (clazz != null) {\n            List<AnnotationInstance> declared = clazz.annotationsMap().get(annotationName);\n            if (declared != null) {\n                annotations.addAll(declared);\n            }\n            DotName superName = clazz.superName();","sourceCodeStart":1841,"sourceCodeEnd":1877,"githubUrl":"https://github.com/quarkusio/quarkus/blob/e1c734241f34c7919086ceb4c9262b4a58f6de44/extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/WebSocketProcessor.java#L1841-L1877","documentation":"Every @OnError callback must accept exactly one parameter recognized as the error (a Throwable subtype). If the callback has zero error parameters or more than one, the build fails, reporting how many were found and the callback signature.","triggerScenarios":"@OnError method with no Throwable parameter (only a connection or message parameter); @OnError method listing two Throwable parameters; error parameter not recognized as an error argument by ErrorCallbackArgument::isError (e.g. not a Throwable subtype).","commonSituations":"Developer writes an @OnError that only logs the connection state and forgets the exception parameter; two Throwable parameters added for cause+exception; parameter typed as Object or String instead of Throwable.","solutions":["Add exactly one parameter that is a Throwable (or subclass) type","Remove any extra Throwable parameters; inspect the cause via getCause() instead","Verify the parameter type extends java.lang.Throwable so it is recognized as the error argument"],"exampleFix":"// before\n@OnError\nvoid onError(WebSocketConnection conn) {}\n\n// after\n@OnError\nvoid onError(WebSocketConnection conn, Exception e) {}\n","handlingStrategy":"validation","validationCode":"for (Method m : MyWebSocket.class.getDeclaredMethods()) {\n    if (m.isAnnotationPresent(OnError.class)) {\n        long n = Arrays.stream(m.getParameterTypes()).filter(Throwable.class::isAssignableFrom).count();\n        if (n != 1) throw new IllegalStateException(m + \" must accept exactly one Throwable parameter\");\n    }\n}","typeGuard":"static boolean hasSingleErrorParam(Method m) {\n    return Arrays.stream(m.getParameterTypes())\n        .filter(Throwable.class::isAssignableFrom).count() == 1;\n}","tryCatchPattern":null,"preventionTips":["Always add a Throwable (or subtype) parameter to @OnError","Never declare two Throwable parameters","Keep the error parameter type as specific as needed but still a Throwable"],"tags":["quarkus","websockets-next","build-time-validation","callback-signature"],"backgroundTag":"callback-parameter-type-mismatch","analyzedSha":"e1c734241f34c7919086ceb4c9262b4a58f6de44","analyzedAt":"2026-09-05T17:01:29.979Z","contentChangedAt":"2026-09-05T17:01:29.979Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}