{"record":{"id":"a371bb5a37be94b0","repo":"alibaba/nacos","slug":"20002-a371bb","errorCode":"20002","errorMessage":"Request header `Request-Module` must be `AI`.","messagePattern":"Request header `Request-Module` must be `AI`\\.","errorType":"exception","errorClass":"NacosApiException","httpStatus":400,"severity":"error","filePath":"ai/src/main/java/com/alibaba/nacos/ai/service/agent/runtime/AgentHttpClientLifecycleService.java","lineNumber":231,"sourceCode":"            attributes == null ? null : attributes.getClientAttribute(IDENTITY_ATTRIBUTE);\n        if (!Objects.equals(boundIdentity, VisibilityHelper.resolveCurrentIdentity())) {\n            throw accessDenied(\"HTTP Client identity does not match its initial binding.\");\n        }\n    }\n    \n    private HttpConnectionBasedClient getClient(String externalClientId) {\n        String internalClientId =\n            HttpConnectionBasedClient.getInternalClientId(externalClientId);\n        Client client = clientManager.getClient(internalClientId);\n        return client instanceof HttpConnectionBasedClient\n            ? (HttpConnectionBasedClient) client : null;\n    }\n    \n    private void validateStatefulHeaders(String externalClientId, String requestModule)\n        throws NacosApiException {\n        validateExternalClientId(externalClientId);\n        if (!Constants.AI.AI_MODULE.equalsIgnoreCase(requestModule)) {\n            throw new NacosApiException(NacosException.INVALID_PARAM,\n                ErrorCode.PARAMETER_VALIDATE_ERROR,\n                \"Request header `\" + HttpHeaderConsts.REQUEST_MODULE + \"` must be `AI`.\");\n        }\n    }\n    \n    private void validateExternalClientId(String externalClientId) throws NacosApiException {\n        if (StringUtils.isBlank(externalClientId)\n            || externalClientId.length() > MAX_EXTERNAL_CLIENT_ID_LENGTH\n            || !EXTERNAL_CLIENT_ID_PATTERN.matcher(externalClientId).matches()) {\n            throw new NacosApiException(NacosException.INVALID_PARAM,\n                ErrorCode.PARAMETER_VALIDATE_ERROR,\n                \"Request header `\" + ClientConstants.HTTP_CLIENT_ID_HEADER\n                    + \"` must match `[A-Za-z0-9._:-]+` and contain 1 to 256 characters.\");\n        }\n    }\n    \n    private ClientLivenessInfo buildLivenessInfo() {\n        // These are the effective fixed intervals used by HttpConnectionBasedClientManager.","sourceCodeStart":213,"sourceCodeEnd":249,"githubUrl":"https://github.com/alibaba/nacos/blob/9b989acdf181d00898f2e8839257bb2b2a3cefe3/ai/src/main/java/com/alibaba/nacos/ai/service/agent/runtime/AgentHttpClientLifecycleService.java#L213-L249","documentation":"Stateful Agent HTTP operations (register, deregister, heartbeat) require the 'Request-Module' HTTP header to be exactly 'AI' (case-insensitive). The AgentHttpClientLifecycleService.validateStatefulHeaders method (line 230) throws this NacosApiException (code 20002, INVALID_PARAM) when the header is missing, blank, or set to any other module name. This routes the request to the correct AI module handler.","triggerScenarios":"Sending an HTTP request to an Agent endpoint registration/deregistration/heartbeat API without the 'Request-Module: AI' header, or with it set to 'Naming', 'Config', or another value.","commonSituations":"SDK or curl command that omits the header; a proxy or gateway that strips custom headers; using a Naming client instead of the AI agent client; header name typo (e.g. 'RequestModule' without the hyphen).","solutions":["Add the HTTP header 'Request-Module: AI' to every stateful Agent HTTP request.","If using an SDK, ensure you are using the AI agent client, not the Naming client.","Verify that no intermediary (load balancer, API gateway) strips the Request-Module header."],"exampleFix":"// before (curl)\ncurl -X POST http://host/v3/admin/ai/agent/endpoints -H 'X-Nacos-Client-Id: my-client' ...\n// after\ncurl -X POST http://host/v3/admin/ai/agent/endpoints -H 'X-Nacos-Client-Id: my-client' -H 'Request-Module: AI' ...","handlingStrategy":"validation","validationCode":"if (!\"AI\".equalsIgnoreCase(requestModule)) {\n    throw new IllegalArgumentException(\"Request-Module header must be AI\");\n}","typeGuard":"public static boolean isValidAiRequestModule(String header) {\n    return \"AI\".equalsIgnoreCase(header);\n}","tryCatchPattern":"try {\n    service.register(clientId, requestModule, batch);\n} catch (NacosApiException e) {\n    if (e.getErrCode() == 20002 && e.getMessage().contains(\"Request-Module\")) {\n        // set the header to AI and retry\n    }\n    throw e;\n}","preventionTips":["Always include the header 'Request-Module: AI' in every stateful Agent HTTP request.","Use the official AI agent SDK which sets this header automatically.","Configure API gateways to preserve the Request-Module header."],"tags":["http","ai-agent","headers","request-validation"],"backgroundTag":null,"analyzedSha":"9b989acdf181d00898f2e8839257bb2b2a3cefe3","analyzedAt":"2026-08-14T07:17:31.569Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}