apache/shenyu · error · ShenyuException
Cannot find the context path(AppName) from the request url
Error message
Cannot find the context path(AppName) from the request url
What it means
When sign skipping is enabled for the exchange, ComposableSignService.handleExchange tries to derive the application name from the first path segment of the raw request URI (used to match paramData entries by appName). If the path splits into no segments (empty or '/'-only path), it throws ShenyuException because the appName cannot be determined and signature verification cannot proceed.
Solutions
- Call the correct backend path including its context-path prefix (e.g. /http-test/order/findById instead of /order/findById) so the first segment identifies the app.
- Configure the 'module' (context path) explicitly in the client/annotation config so skipSignExchange does not need to parse it from the URL.
- If this is a health-check probe, exclude the probe path from the sign plugin's selector matching.
- Add a gateway-side check to return 404 for root-path requests before the sign plugin executes.
Example fix
// before curl http://gateway:9195/ // after curl http://gateway:9195/my-app-context/order/findById
Defensive patterns
Strategy: validation
Validate before calling
URI uri = request.getURI();
String path = uri.getRawPath();
if (path == null || path.split("/").length < 2) {
throw new IllegalArgumentException("request path must include the app context path, e.g. /my-app/resource");
} Try / catch
try {
return signService.signatureVerify(exchange);
} catch (ShenyuException e) {
if (e.getMessage().contains("context path")) {
return WebFluxResultUtils.failedResult(...); // 404-style hint: path lacks context prefix
}
throw e;
} Prevention
- Always call backends through their context-path prefix when sign skip logic derives appName from the URL.
- Configure the client module/context path explicitly so URL parsing is not required.
- Exclude health-check/probe paths from sign plugin selectors.
When it happens
Trigger: A request with a raw path that is empty or '/' (no context-path segment) hits the sign plugin while skipSign is enabled, so the code path that extracts contextPath[0] as the appName executes against an empty split array during signatureVerify.
Common situations: Health checks or clients probing the gateway root URL ('/' or '') while the sign plugin is active with skipSign configured; clients calling the gateway without the backend context path prefix; misconfigured route paths that strip the context path.
Related errors
- -114
- 401
- shenyu.jwt.secretKey is not configured. In a multi-instance…
- shenyu discovery mode current didn't support
- websocket on client open failed, namespaceId is null
AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12).
Data as JSON: /api/errors/b80e8fb8cec2be29.
Report an issue: GitHub.
Appendix: source
Thrown at shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/service/ComposableSignService.java:220
return VerifyResult.fail(Constants.SIGN_VALUE_IS_ERROR);
}
return VerifyResult.success();
}
private void handleExchange(final ServerWebExchange exchange,
final AppAuthData appAuthData,
final ShenyuContext context) {
List<AuthParamData> paramDataList = appAuthData.getParamDataList();
if (!CollectionUtils.isEmpty(paramDataList)) {
String realAppName;
if (skipSignExchange(context)) {
String rawPath = exchange.getRequest().getURI().getRawPath();
// get the context path from the request url
String[] contextPath = StringUtils.split(rawPath, "/");
if (ArrayUtils.isEmpty(contextPath)) {
throw new ShenyuException("Cannot find the context path(AppName) from the request url");
}
realAppName = contextPath[0];
} else {
realAppName = context.getModule();
}
paramDataList.stream().filter(p -> p.getAppName().equals(realAppName))
.map(AuthParamData::getAppParam)
.filter(StringUtils::isNoneBlank).findFirst()
.ifPresent(param -> exchange.getRequest().mutate().headers(httpHeaders -> httpHeaders.set(Constants.APP_PARAM, param)).build());
}
}
private boolean skipSignExchange(final ShenyuContext context) {
return StringUtils.equals(String.format("%s-%s", PluginEnum.SPRING_CLOUD.getName(), context.getRpcType()), context.getModule())
|| StringUtils.equals(String.format("%s-%s", PluginEnum.DIVIDE.getName(), context.getRpcType()), context.getModule())
|| StringUtils.equals(String.format("%s-%s", PluginEnum.WEB_SOCKET.getName(), context.getRpcType()), context.getModule());
}
}View on GitHub (pinned to 567142e072)