{"record":{"id":"ecfb253ca944f487","repo":"apache/rocketmq","slug":"topic-header-is-null","errorCode":null,"errorMessage":"topic header is null.","messagePattern":"topic header is null\\.","errorType":"exception","errorClass":"AuthorizationException","httpStatus":null,"severity":"error","filePath":"auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilder.java","lineNumber":527,"sourceCode":"                        command, SubscriptionGroupList.class, \"subscription group list\");\n                    if (CollectionUtils.isEmpty(subscriptionGroupList.getGroupConfigList())) {\n                        throw new AuthorizationException(\"subscription group list is empty.\");\n                    }\n                    Set<String> subscriptionGroupResources = new LinkedHashSet<>();\n                    for (SubscriptionGroupConfig groupConfig : subscriptionGroupList.getGroupConfigList()) {\n                        if (groupConfig == null) {\n                            throw new AuthorizationException(\"subscription group config is null.\");\n                        }\n                        addUniqueContext(result, subscriptionGroupResources, subject,\n                            Resource.ofGroup(requireResource(groupConfig.getGroupName(), \"consumer group\")),\n                            Action.CREATE, sourceIp);\n                    }\n                    break;\n                case RequestCode.UPDATE_AND_CREATE_STATIC_TOPIC:\n                    CreateTopicRequestHeader createTopicRequestHeader =\n                        command.decodeCommandCustomHeader(CreateTopicRequestHeader.class);\n                    if (createTopicRequestHeader == null) {\n                        throw new AuthorizationException(\"topic header is null.\");\n                    }\n                    String staticTopic = requireResource(createTopicRequestHeader.getTopic(), \"topic\");\n                    TopicQueueMappingDetail mappingDetail = decodeRequiredBody(\n                        command, TopicQueueMappingDetail.class, \"topic queue mapping\");\n                    if (!StringUtils.equals(\n                        staticTopic, requireResource(mappingDetail.getTopic(), \"mapping topic\"))) {\n                        throw new AuthorizationException(\"mapping topic does not match topic header.\");\n                    }\n                    topic = Resource.ofTopic(staticTopic);\n                    result.add(DefaultAuthorizationContext.of(subject, topic, Action.CREATE, sourceIp));\n                    break;\n                case RequestCode.GET_BROKER_CONFIG:\n                case RequestCode.GET_BROKER_RUNTIME_INFO:\n                case RequestCode.GET_ALL_CONSUMER_OFFSET:\n                case RequestCode.GET_TIMER_CHECK_POINT:\n                case RequestCode.GET_ALL_DELAY_OFFSET:\n                case RequestCode.GET_BROKER_HA_STATUS:\n                case RequestCode.GET_BROKER_EPOCH_CACHE:","sourceCodeStart":509,"sourceCodeEnd":545,"githubUrl":"https://github.com/apache/rocketmq/blob/293f5885719fc4aa3619446a1900f58ccfcfdd29/auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilder.java#L509-L545","documentation":"Thrown for UPDATE_AND_CREATE_STATIC_TOPIC when command.decodeCommandCustomHeader(CreateTopicRequestHeader.class) returns null — i.e. the remoting command lacks the custom header fields needed to build a CreateTopicRequestHeader. The topic name from that header is the Resource the CREATE action is authorized against, so a null header cannot be authorized and the request fails.","triggerScenarios":"Sending RequestCode.UPDATE_AND_CREATE_STATIC_TOPIC where the command's extFields do not contain the entries CreateTopicRequestHeader maps (e.g. missing 'topic'), or where the header class cannot be instantiated/populated. Usually means the request was constructed by raw RemotingCommand without setCommandCustomHeader, or by an incompatible client version.","commonSituations":"Custom tooling that builds RemotingCommand manually for static (migrated) topic creation and forgets the header; version skew where the header field names changed; proxying frameworks that drop extFields.","solutions":["Construct the request via the standard client API (e.g. AdminBrokerProcessor / mqadmin createTopic -c) so the CreateTopicRequestHeader is always attached.","If building RemotingCommand manually, call command.setCommandCustomHeader(new CreateTopicRequestHeader(topic...)) before sending.","Check that the 'topic' extField is present and the client version matches the broker's static-topic support."],"exampleFix":"// before\nRemotingCommand cmd = RemotingCommand.createRequestCommand(\n    RequestCode.UPDATE_AND_CREATE_STATIC_TOPIC, null); // no header\n\n// after\nCreateTopicRequestHeader h = new CreateTopicRequestHeader();\nh.setTopic(topic);\nRemotingCommand cmd = RemotingCommand.createRequestCommand(\n    RequestCode.UPDATE_AND_CREATE_STATIC_TOPIC, h);","handlingStrategy":"validation","validationCode":"RemotingCommand cmd = RemotingCommand.createRequestCommand(\n    RequestCode.UPDATE_AND_CREATE_STATIC_TOPIC, header);\nif (cmd.readCustomHeader() == null) { throw new IllegalStateException(\"header missing\"); }","typeGuard":null,"tryCatchPattern":"try { client.invokeSync(addr, cmd, timeout); }\ncatch (AuthorizationException e) {\n    if (\"topic header is null.\".equals(e.getMessage())) { attachHeaderAndResend(); return; }\n    throw e;\n}","preventionTips":["Always create request commands with the typed header instance","Prefer the official admin client over hand-built RemotingCommand"],"tags":["rocketmq","authorization","static-topic","remoting-header"],"backgroundTag":null,"analyzedSha":"293f5885719fc4aa3619446a1900f58ccfcfdd29","analyzedAt":"2026-08-14T11:50:13.822Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}