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

  1. 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.
  2. Configure the 'module' (context path) explicitly in the client/annotation config so skipSignExchange does not need to parse it from the URL.
  3. If this is a health-check probe, exclude the probe path from the sign plugin's selector matching.
  4. 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

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


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)