apache/shenyu · critical · ShenyuException

websocket sync token is not configured

Error message

websocket sync token is not configured

What it means

WebsocketConfigurator.modifyHandshake authenticates gateway websocket sync clients by comparing the X-Shenyu-Sync-Token header against the token configured in shenyu.sync.websocket.token on the admin. checkSyncToken throws ShenyuException when the admin has no token configured at all, aborting the handshake. ShenYu requires the token for websocket sync security.

Solutions

  1. Set a token in admin's application.yml: shenyu.sync.websocket.token: <secret>.
  2. Set the same token in the gateway config (shenyu.sync.websocket.token / websocket sync token) so the handshake passes the next check.
  3. Restart shenyu-admin after adding the property so WebsocketSyncProperties is repopulated.
  4. If deploying via environment variables, ensure SHENYU_SYNC_WEBSOCKET_TOKEN is actually injected into the admin container.

Example fix

// before (admin application.yml)
shenyu:
  sync:
    websocket:
      enabled: true
// after
shenyu:
  sync:
    websocket:
      enabled: true
      token: "mySharedSecret"
Defensive patterns

Strategy: validation

Validate before calling

// startup check (admin)
@PostConstruct
void check() {
    if (websocketSyncProperties.getToken() == null || websocketSyncProperties.getToken().isBlank())
        throw new IllegalStateException("shenyu.sync.websocket.token must be set");
}

Try / catch

try {
    modifyHandshake(...); // server side
} catch (ShenyuException e) {
    LOG.error("websocket handshake rejected: {}", e.getMessage());
}

Prevention

When it happens

Trigger: A gateway attempts the websocket sync handshake while admin's `shenyu.sync.websocket.token` property is absent or empty in application.yml.

Common situations: Fresh admin deployment with a minimal application.yml missing the token; operator deleted the token thinking it optional; upgraded admin where token became mandatory but old config kept; running admin with default config from an older example file.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


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

Appendix: source

Thrown at shenyu-admin/src/main/java/org/apache/shenyu/admin/listener/websocket/WebsocketConfigurator.java:101

        }
        return super.checkOrigin(originHeaderValue);
    }

    @Override
    public void onStartup(final ServletContext servletContext) {
        int messageMaxSize = getWebsocketSyncProperties().getMessageMaxSize();
        if (messageMaxSize > 0) {
            servletContext.setInitParameter(TEXT_BUFFER_SIZE_SERVLET_CONTEXT_INIT_PARAM,
                    String.valueOf(messageMaxSize));
            servletContext.setInitParameter(BINARY_BUFFER_SIZE_SERVLET_CONTEXT_INIT_PARAM,
                    String.valueOf(messageMaxSize));
        }
    }

    private void checkSyncToken(final HandshakeRequest request) {
        String configuredToken = getWebsocketSyncProperties().getToken();
        if (StringUtils.isBlank(configuredToken)) {
            throw new ShenyuException("websocket sync token is not configured");
        }
        String requestToken = getHeader(request.getHeaders(), Constants.X_SHENYU_SYNC_TOKEN);
        if (StringUtils.isBlank(requestToken) || !isSameToken(configuredToken, requestToken)) {
            throw new ShenyuException("websocket sync token is invalid");
        }
    }

    private WebsocketSyncProperties getWebsocketSyncProperties() {
        return Optional.ofNullable(websocketSyncProperties)
                .orElseGet(() -> SpringBeanUtils.getInstance().getBean(WebsocketSyncProperties.class));
    }

    private boolean isSameToken(final String configuredToken, final String requestToken) {
        return MessageDigest.isEqual(
                configuredToken.getBytes(StandardCharsets.UTF_8),
                requestToken.getBytes(StandardCharsets.UTF_8));
    }

View on GitHub (pinned to 567142e072)