{"record":{"id":"d4e7f841f63dcbc7","repo":"apache/rocketmq","slug":"authentication-header-is-incorrect","errorCode":null,"errorMessage":"authentication header is incorrect.","messagePattern":"authentication header is incorrect\\.","errorType":"exception","errorClass":"AuthenticationException","httpStatus":null,"severity":"error","filePath":"auth/src/main/java/org/apache/rocketmq/auth/authentication/builder/DefaultAuthenticationContextBuilder.java","lineNumber":64,"sourceCode":"\n    @Override\n    public DefaultAuthenticationContext build(Metadata metadata, GeneratedMessageV3 request) {\n        try {\n            DefaultAuthenticationContext context = new DefaultAuthenticationContext();\n            context.setChannelId(metadata.get(GrpcConstants.CHANNEL_ID));\n            context.setRpcCode(request.getDescriptorForType().getFullName());\n            String authorization = metadata.get(GrpcConstants.AUTHORIZATION);\n            if (StringUtils.isEmpty(authorization)) {\n                return context;\n            }\n            String datetime = metadata.get(GrpcConstants.DATE_TIME);\n            if (StringUtils.isEmpty(datetime)) {\n                throw new AuthenticationException(\"datetime is null.\");\n            }\n\n            String[] result = authorization.split(CommonConstants.SPACE, 2);\n            if (result.length != 2) {\n                throw new AuthenticationException(\"authentication header is incorrect.\");\n            }\n            String[] keyValues = result[1].split(CommonConstants.COMMA);\n            for (String keyValue : keyValues) {\n                String[] kv = keyValue.trim().split(CommonConstants.EQUAL, 2);\n                int kvLength = kv.length;\n                if (kv.length != 2) {\n                    throw new AuthenticationException(\"authentication keyValues length is incorrect, actual length={}.\", kvLength);\n                }\n                String authItem = kv[0];\n                if (CREDENTIAL.equals(authItem)) {\n                    String[] credential = kv[1].split(CommonConstants.SLASH);\n                    int credentialActualLength = credential.length;\n                    if (credentialActualLength == 0) {\n                        throw new AuthenticationException(\"authentication credential length is incorrect, actual length={}.\", credentialActualLength);\n                    }\n                    context.setUsername(credential[0]);\n                    continue;\n                }","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/apache/rocketmq/blob/293f5885719fc4aa3619446a1900f58ccfcfdd29/auth/src/main/java/org/apache/rocketmq/auth/authentication/builder/DefaultAuthenticationContextBuilder.java#L46-L82","documentation":"The Authorization header was split on a single space with limit 2 and did not yield two parts. The expected format is a scheme token followed by the credential string, i.e. 'RocketMQ Credential=<user>,Signature=<sig>,DateTime=<datetime>'. Any header lacking the scheme prefix (or lacking any space) makes result.length != 2 and this exception is thrown.","triggerScenarios":"Calling a gRPC API with authorization metadata such as 'Credential=abc,Signature=xyz' (missing the leading 'RocketMQ ' scheme token), an empty scheme, or multiple spaces misplacing the split so that only one token results.","commonSituations":"Custom clients copying the AWS-style header without the 'RocketMQ' scheme; SDKs upgraded from 4.x to 5.x where the ACL header format changed; test harnesses hard-coding a malformed header.","solutions":["Format the header as 'RocketMQ Credential=<username>,Signature=<signature>,DateTime=<datetime>' with exactly one space after the RocketMQ scheme word.","Delegate header construction to the client SDK's built-in signer/interceptor rather than building the string manually.","Log the outgoing authorization header (redacted) once to confirm it starts with 'RocketMQ '."],"exampleFix":"// before\nString authorization = \"Credential=\" + user + \",Signature=\" + sig;\n\n// after\nString authorization = \"RocketMQ Credential=\" + user + \",Signature=\" + sig + \",DateTime=\" + datetime;","handlingStrategy":"validation","validationCode":"private static final Pattern AUTH_HEADER = Pattern.compile(\"^RocketMQ \\S+=\\S+(,\\S+=\\S+)*$\");\nif (!AUTH_HEADER.matcher(authorization).matches()) {\n    throw new IllegalArgumentException(\"authorization must be 'RocketMQ Credential=...,Signature=...,DateTime=...'\");\n}","typeGuard":null,"tryCatchPattern":"catch (AuthenticationException e) { if (e.getMessage().contains(\"header is incorrect\")) fail fast with a config error - this is never transient; rethrow otherwise. }","preventionTips":["Never hand-concatenate the authorization header; use the SDK signer","Keep a golden-sample header in tests and assert the outgoing header equals it"],"tags":["rocketmq","authentication","header-format","grpc"],"backgroundTag":null,"analyzedSha":"293f5885719fc4aa3619446a1900f58ccfcfdd29","analyzedAt":"2026-08-14T11:50:13.822Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}