{"record":{"id":"b80e8fb8cec2be29","repo":"apache/shenyu","slug":"cannot-find-the-context-path-appname-from-the-request-url","errorCode":null,"errorMessage":"Cannot find the context path(AppName) from the request url","messagePattern":"Cannot find the context path\\(AppName\\) from the request url","errorType":"exception","errorClass":"ShenyuException","httpStatus":null,"severity":"error","filePath":"shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/service/ComposableSignService.java","lineNumber":220,"sourceCode":"            return VerifyResult.fail(Constants.SIGN_VALUE_IS_ERROR);\n        }\n        return VerifyResult.success();\n    }\n\n    private void handleExchange(final ServerWebExchange exchange,\n                                final AppAuthData appAuthData,\n                                final ShenyuContext context) {\n\n        List<AuthParamData> paramDataList = appAuthData.getParamDataList();\n\n        if (!CollectionUtils.isEmpty(paramDataList)) {\n            String realAppName;\n            if (skipSignExchange(context)) {\n                String rawPath = exchange.getRequest().getURI().getRawPath();\n                // get the context path from the request url\n                String[] contextPath = StringUtils.split(rawPath, \"/\");\n                if (ArrayUtils.isEmpty(contextPath)) {\n                    throw new ShenyuException(\"Cannot find the context path(AppName) from the request url\");\n                }\n                realAppName = contextPath[0];\n            } else {\n                realAppName = context.getModule();\n            }\n            paramDataList.stream().filter(p -> p.getAppName().equals(realAppName))\n                    .map(AuthParamData::getAppParam)\n                    .filter(StringUtils::isNoneBlank).findFirst()\n                    .ifPresent(param -> exchange.getRequest().mutate().headers(httpHeaders -> httpHeaders.set(Constants.APP_PARAM, param)).build());\n        }\n    }\n\n    private boolean skipSignExchange(final ShenyuContext context) {\n        return StringUtils.equals(String.format(\"%s-%s\", PluginEnum.SPRING_CLOUD.getName(), context.getRpcType()), context.getModule())\n                || StringUtils.equals(String.format(\"%s-%s\", PluginEnum.DIVIDE.getName(), context.getRpcType()), context.getModule())\n                || StringUtils.equals(String.format(\"%s-%s\", PluginEnum.WEB_SOCKET.getName(), context.getRpcType()), context.getModule());\n    }\n}","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/apache/shenyu/blob/567142e07261b3e615ae8850b30f4421f455cc5d/shenyu-plugin/shenyu-plugin-security/shenyu-plugin-sign/src/main/java/org/apache/shenyu/plugin/sign/service/ComposableSignService.java#L202-L238","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ncurl http://gateway:9195/\n// after\ncurl http://gateway:9195/my-app-context/order/findById","handlingStrategy":"validation","validationCode":"URI uri = request.getURI();\nString path = uri.getRawPath();\nif (path == null || path.split(\"/\").length < 2) {\n    throw new IllegalArgumentException(\"request path must include the app context path, e.g. /my-app/resource\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    return signService.signatureVerify(exchange);\n} catch (ShenyuException e) {\n    if (e.getMessage().contains(\"context path\")) {\n        return WebFluxResultUtils.failedResult(...); // 404-style hint: path lacks context prefix\n    }\n    throw e;\n}","preventionTips":["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."],"tags":["signature","gateway-plugin","url-path","configuration"],"backgroundTag":"missing-context-for-path","analyzedSha":"567142e07261b3e615ae8850b30f4421f455cc5d","analyzedAt":"2026-09-12T10:08:21.293Z","contentChangedAt":"2026-09-12T10:08:21.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}