{"record":{"id":"438010483888ada5","repo":"apache/shenyu","slug":"class-missing-annotation-shenyuserverendpoint-class-name","errorCode":null,"errorMessage":"Class missing annotation ShenyuServerEndpoint! class name: ","messagePattern":"Class missing annotation ShenyuServerEndpoint! class name: ","errorType":"exception","errorClass":"ShenyuException","httpStatus":null,"severity":"error","filePath":"shenyu-client/shenyu-client-websocket/shenyu-client-spring-websocket/src/main/java/org/apache/shenyu/client/spring/websocket/init/ShenyuServerEndpointerExporter.java","lineNumber":79,"sourceCode":"    protected void initServletContext(final ServletContext servletContext) {\n        if (Objects.isNull(this.serverContainer)) {\n            this.serverContainer = (ServerContainer) servletContext.getAttribute(\"jakarta.websocket.server.ServerContainer\");\n        }\n    }\n\n    @Override\n    protected boolean isContextRequired() {\n        return false;\n    }\n\n    /**\n     * Register endpoint.\n     * @param pojo pojo\n     */\n    public void registerEndpoint(final Class<?> pojo) {\n        ShenyuServerEndpoint annotation = AnnotatedElementUtils.findMergedAnnotation(pojo, ShenyuServerEndpoint.class);\n        if (Objects.isNull(annotation)) {\n            throw new ShenyuException(\"Class missing annotation ShenyuServerEndpoint! class name: \" + pojo.getName());\n        }\n\n        String path = annotation.value();\n        Class<? extends ServerEndpointConfig.Configurator> configuratorClazz = annotation.configurator();\n        ServerEndpointConfig.Configurator configurator = null;\n        if (!configuratorClazz.equals(ServerEndpointConfig.Configurator.class)) {\n            try {\n                configurator = annotation.configurator().getConstructor().newInstance();\n            } catch (ReflectiveOperationException ex) {\n                LOG.error(\"ShenyuServerEndpoint configurator init fail! Class name: {}, configurator name: {}\", pojo.getName(), annotation.configurator().getName());\n                throw new ShenyuException(ex);\n            }\n        }\n        ServerEndpointConfig sec = ServerEndpointConfig.Builder.create(pojo, path)\n                .decoders(Arrays.asList(annotation.decoders()))\n                .encoders(Arrays.asList(annotation.encoders()))\n                .subprotocols(Arrays.asList(annotation.subprotocols()))\n                .configurator(configurator).build();","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/apache/shenyu/blob/567142e07261b3e615ae8850b30f4421f455cc5d/shenyu-client/shenyu-client-websocket/shenyu-client-spring-websocket/src/main/java/org/apache/shenyu/client/spring/websocket/init/ShenyuServerEndpointerExporter.java#L61-L97","documentation":"ShenyuServerEndpointerExporter.registerEndpoint() requires each candidate class to be annotated with @ShenyuServerEndpoint. When AnnotatedElementUtils.findMergedAnnotation returns null, the class cannot be converted into a websocket ServerEndpointConfig for gateway registration, so a ShenyuException naming the offending class is thrown.","triggerScenarios":"registerEndpoint(pojo) is called (directly or via registerEndpointsBeans scanning) with a bean class that lacks the @ShenyuServerEndpoint annotation — e.g. a plain @ServerEndpoint JSR-356 class or an arbitrary bean picked up by the endpoint scan.","commonSituations":"Developers annotating websocket handlers only with javax/jakarta @ServerEndpoint and forgetting the ShenYu-specific @ShenyuServerEndpoint; passing non-endpoint helper beans into registerEndpoint; bean scanning picking up unrelated classes in the configured base packages.","solutions":["Annotate the websocket endpoint class with @ShenyuServerEndpoint(\"/your/path\") in addition to @ServerEndpoint.","Check the exception's 'class name:' suffix to find the offending class and confirm it is the intended endpoint.","Narrow the scanned base packages so unrelated beans are not passed to registerEndpoint.","If the class is intentionally not an endpoint, remove it from the beans passed to the exporter."],"exampleFix":"// before\n@ServerEndpoint(\"/websocket/echo\")\npublic class EchoServer { ... }\n// after\n@ServerEndpoint(\"/websocket/echo\")\n@ShenyuServerEndpoint(\"/websocket/echo\")\npublic class EchoServer { ... }","handlingStrategy":"validation","validationCode":"Class<?> pojo = ...;\nif (AnnotatedElementUtils.findMergedAnnotation(pojo, ShenyuServerEndpoint.class) == null) {\n    throw new IllegalArgumentException(pojo.getName() + \" must be annotated with @ShenyuServerEndpoint\");\n}","typeGuard":"boolean isShenyuEndpoint(Class<?> c) {\n    return AnnotatedElementUtils.findMergedAnnotation(c, ShenyuServerEndpoint.class) != null;\n}","tryCatchPattern":"try {\n    exporter.registerEndpoint(pojo);\n} catch (ShenyuException e) {\n    LOG.error(\"Endpoint registration failed: {}\", e.getMessage());\n}","preventionTips":["Always pair @ServerEndpoint with @ShenyuServerEndpoint on gateway-exposed websocket handlers.","Add an ArchUnit/reflection scan test asserting all scanned websocket beans carry the annotation.","Limit component scanning base packages to classes that are actual endpoints."],"tags":["websocket","annotation","client-registration"],"backgroundTag":"missing-required-argument","analyzedSha":"567142e07261b3e615ae8850b30f4421f455cc5d","analyzedAt":"2026-09-12T10:08:21.293Z","contentChangedAt":"2026-09-12T10:08:21.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}