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
- Set a token in admin's application.yml: shenyu.sync.websocket.token: <secret>.
- Set the same token in the gateway config (shenyu.sync.websocket.token / websocket sync token) so the handshake passes the next check.
- Restart shenyu-admin after adding the property so WebsocketSyncProperties is repopulated.
- 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
- Always set shenyu.sync.websocket.token in admin and gateway configs
- Inject the token via secret/env management rather than deleting it
- Add a deployment smoke test that opens the websocket sync connection
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
- websocket on client open failed, namespaceId is null
- websocket sync token is invalid
- shenyu.jwt.secretKey is not configured. In a multi-instance…
- shenyu discovery mode current didn't support
- namespaceId can not be null
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)