apache/shenyu · error · ShenyuException

websocket on client open failed, namespaceId is null

Error message

websocket on client open failed, namespaceId is null

What it means

WebsocketCollector.onOpen registers a gateway websocket session into a per-namespace session map. The namespaceId is read from the session's request parameters; if it is missing or blank the connection cannot be attributed to a namespace, so onOpen throws ShenyuException, causing the websocket handshake/handler to fail for that client.

Solutions

  1. Configure the gateway's shenyu.websocket sync url to include the namespaceId query parameter, e.g. ws://admin:9095/websocket?namespaceId=649330b6-v2.
  2. Check shenyu.sync.websocket properties on the gateway side and admin's namespace config so both agree on the namespace id.
  3. Upgrade the gateway to a version matching the admin's namespace support.
  4. Verify any reverse proxy in front of admin preserves the query string on websocket upgrade requests.

Example fix

// before (application.yml, gateway)
shenyu:
  sync:
    websocket:
      url: ws://admin:9095/websocket
// after
shenyu:
  sync:
    websocket:
      url: ws://admin:9095/websocket?namespaceId=649330b6-v2
Defensive patterns

Strategy: validation

Validate before calling

URI uri = URI.create(syncUrl);
Map<String,String> q = parseQuery(uri.getRawQuery());
if (q.get("namespaceId") == null || q.get("namespaceId").isBlank()) throw new IllegalStateException("websocket sync url missing namespaceId");

Try / catch

try {
    session = container.connectToServer(...);
} catch (DeploymentException e) {
    LOG.error("websocket open failed — check namespaceId query param on the sync url", e);
}

Prevention

When it happens

Trigger: A gateway opens the admin websocket sync connection (/websocket) without providing a `namespaceId` parameter (e.g. ws://admin:9095/websocket without ?namespaceId=...), or with an empty/blank namespaceId.

Common situations: Older gateway clients predating multi-namespace support connecting to a namespace-aware admin; hand-written websocket clients omitting the query parameter; reverse proxy stripping query strings from the upgrade request.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12). Data as JSON: /api/errors/6bdc4f70ec581254. Report an issue: GitHub.

Appendix: source

Thrown at shenyu-admin/src/main/java/org/apache/shenyu/admin/listener/websocket/WebsocketCollector.java:89

    private static final Map<Session, SessionSendQueue> SESSION_SEND_QUEUES = Maps.newConcurrentMap();
    
    private static final String SESSION_KEY = "sessionKey";
    
    /**
     * On open.
     *
     * @param session the session
     */
    @OnOpen
    public void onOpen(final Session session) {
        String clientIp = getClientIp(session);
        LOG.info("websocket on client[{}] open successful, maxTextMessageBufferSize: {}",
                clientIp, session.getMaxTextMessageBufferSize());
        SESSION_SET.add(session);
        
        String namespaceId = getNamespaceId(session);
        if (StringUtils.isBlank(namespaceId)) {
            throw new ShenyuException("websocket on client open failed, namespaceId is null");
        }
        LOG.info("websocket on client[{}] open successful, namespaceId: {}", clientIp, namespaceId);
        NAMESPACE_SESSION_MAP.computeIfAbsent(namespaceId, k -> Sets.newConcurrentHashSet()).add(session);
    }
    
    private static String getClientIp(final Session session) {
        if (!session.isOpen()) {
            return StringUtils.EMPTY;
        }
        Map<String, Object> userProperties = session.getUserProperties();
        if (MapUtils.isEmpty(userProperties)) {
            return StringUtils.EMPTY;
        }
        
        return Optional.ofNullable(userProperties.get(WebsocketListener.CLIENT_IP_NAME))
                .map(Object::toString)
                .orElse(StringUtils.EMPTY);
    }

View on GitHub (pinned to 567142e072)