quarkusio/quarkus · error · WebSocketException
Endpoint class may not be annotated with both @WebSocket…
Error message
Endpoint class may not be annotated with both @WebSocket and @WebSocketClient:
What it means
A bean class annotated with @WebSocket is a server endpoint and with @WebSocketClient is a client endpoint; these are mutually exclusive. Quarkus rejects the deployment when both annotations are present because the endpoint's target (server vs client), path registration and lifecycle cannot be determined.
Solutions
- Remove either @WebSocket or @WebSocketClient from the class, keeping the one that matches its role
- Split the shared logic into two classes, one per annotation
- Check imports — io.quarkus.websockets.next.WebSocket vs WebSocketClient
Example fix
// before
@WebSocket(path = "/ws")
@WebSocketClient(path = "/client")
class MyWs {}
// after
@WebSocket(path = "/ws")
class MyWs {} Defensive patterns
Strategy: validation
Validate before calling
// Compile-time-ish check in a shared base or unit test
void assertSingleWsAnnotation(Class<?> c) {
boolean server = c.isAnnotationPresent(WebSocket.class);
boolean client = c.isAnnotationPresent(WebSocketClient.class);
if (server && client) throw new IllegalStateException(c + " has both @WebSocket and @WebSocketClient");
} Prevention
- Decide server vs client role per class; never share one annotated class for both
- Double-check imports when copy-pasting between server and client packages
- Split shared logic into a plain superclass consumed by two role-specific subclasses
When it happens
Trigger: Annotating the same class with both @WebSocket and @WebSocketClient, e.g. when sharing one class between a server and client test, or copy-pasting a client class and adding @WebSocket on top.
Common situations: Code sharing between server and client modules; accidental import of the wrong annotation causing a double annotation; refactoring an endpoint into a client and leaving the old annotation behind.
Related errors
- Only one of the ' ' annotations can be applied on the ' '…
- Only one of the ' ' annotations can be applied on the ' '…
- @PathParam name [ ] must be used in the endpoint path [ ]
- @PermissionChecker annotation placed on the
- Security annotations
AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05).
Data as JSON: /api/errors/728394959a75fc04.
Report an issue: GitHub.
Appendix: source
Thrown at extensions/websockets-next/deployment/src/main/java/io/quarkus/websockets/next/deployment/WebSocketProcessor.java:344
BuildProducer<WebSocketEndpointBuildItem> endpoints) {
IndexView index = beanArchiveIndex.getIndex();
// Collect WebSocket endpoints
Map<String, DotName> serverIdToEndpoint = new HashMap<>();
Map<String, DotName> serverPathToEndpoint = new HashMap<>();
Map<String, DotName> clientIdToEndpoint = new HashMap<>();
Map<String, DotName> clientPathToEndpoint = new HashMap<>();
for (BeanInfo bean : beanDiscoveryFinished.beanStream().classBeans()) {
ClassInfo beanClass = bean.getTarget().get().asClass();
AnnotationInstance webSocketAnnotation = beanClass.annotation(WebSocketDotNames.WEB_SOCKET);
AnnotationInstance webSocketClientAnnotation = beanClass.annotation(WebSocketDotNames.WEB_SOCKET_CLIENT);
if (webSocketAnnotation == null && webSocketClientAnnotation == null) {
continue;
} else if (webSocketAnnotation != null && webSocketClientAnnotation != null) {
throw new WebSocketException(
"Endpoint class may not be annotated with both @WebSocket and @WebSocketClient: " + beanClass);
}
String path;
String id;
AnnotationValue inboundProcessingMode;
Target target;
if (webSocketAnnotation != null) {
target = Target.SERVER;
path = getPath(webSocketAnnotation.value("path").asString());
if (beanClass.nestingType() == NestingType.INNER) {
// Sub-websocket - merge the path from the enclosing classes
path = mergePath(getPathPrefix(index, beanClass.enclosingClass()), path);
}
DotName prevPath = serverPathToEndpoint.put(path, beanClass.name());
if (prevPath != null) {
throw new WebSocketServerException(
String.format("Multiple endpoints [%s, %s] define the same path: %s", prevPath, beanClass, path));View on GitHub (pinned to e1c734241f)