{"record":{"id":"fc07e8e8167b0429","repo":"apache/rocketmq","slug":"topic-list-is-empty","errorCode":null,"errorMessage":"topic list is empty.","messagePattern":"topic list is empty\\.","errorType":"exception","errorClass":"AuthorizationException","httpStatus":null,"severity":"error","filePath":"auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilder.java","lineNumber":466,"sourceCode":"                            throw new AuthorizationException(\"lite subscription is null.\");\n                        }\n                        addUniqueContext(result, liteSubscriptionResources, subject,\n                            Resource.ofGroup(requireResource(subscription.getGroup(), \"consumer group\")),\n                            Action.SUB, sourceIp);\n                        addUniqueContext(result, liteSubscriptionResources, subject,\n                            Resource.ofTopic(requireResource(subscription.getTopic(), \"topic\")),\n                            Action.SUB, sourceIp);\n                    }\n                    break;\n                case RequestCode.UPDATE_BROKER_CONFIG:\n                    result.add(DefaultAuthorizationContext.of(subject,\n                        Resource.ofCluster(authConfig.getClusterName()), Action.UPDATE, sourceIp));\n                    break;\n                case RequestCode.UPDATE_AND_CREATE_TOPIC_LIST:\n                    CreateTopicListRequestBody topicListBody = decodeRequiredBody(\n                        command, CreateTopicListRequestBody.class, \"topic list\");\n                    if (CollectionUtils.isEmpty(topicListBody.getTopicConfigList())) {\n                        throw new AuthorizationException(\"topic list is empty.\");\n                    }\n                    Set<String> topicListResources = new LinkedHashSet<>();\n                    for (TopicConfig topicConfig : topicListBody.getTopicConfigList()) {\n                        if (topicConfig == null) {\n                            throw new AuthorizationException(\"topic config is null.\");\n                        }\n                        String topicName = requireResource(topicConfig.getTopicName(), \"topic\");\n                        Resource resource = NamespaceUtil.isRetryTopic(topicName)\n                            ? Resource.ofGroup(topicName) : Resource.ofTopic(topicName);\n                        addUniqueContext(result, topicListResources, subject, resource, Action.CREATE, sourceIp);\n                    }\n                    break;\n                case RequestCode.UPDATE_COLD_DATA_FLOW_CTR_CONFIG:\n                    Properties properties = MixAll.string2Properties(\n                        decodeRequiredText(command, \"cold data flow config\"));\n                    if (properties == null || properties.isEmpty()) {\n                        throw new AuthorizationException(\"cold data flow config is empty.\");\n                    }","sourceCodeStart":448,"sourceCodeEnd":484,"githubUrl":"https://github.com/apache/rocketmq/blob/293f5885719fc4aa3619446a1900f58ccfcfdd29/auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilder.java#L448-L484","documentation":"Thrown by DefaultAuthorizationContextBuilder when building an authorization context for an UPDATE_AND_CREATE_TOPIC_LIST admin request whose decoded CreateTopicListRequestBody contains an empty (or null) topicConfigList. Before authorizing a CREATE action on each topic, the builder must derive one Resource per TopicConfig; an empty list leaves nothing to authorize, so the request is rejected with AuthorizationException instead of being silently allowed. This is a client-side payload validation failure, not a permission denial.","triggerScenarios":"A client (e.g. AdminTool / mqadmin updateTopicList, or AdminBrokerProcessor path for RequestCode.UPDATE_AND_CREATE_TOPIC_LIST == 317) sends a CreateTopicListRequestBody whose serialized JSON has topicConfigList absent, null, or []. It can also fire if the body JSON deserializes but the field name is misspelled so the list stays null after decode.","commonSituations":"Scripts migrating topics in bulk that loop over an empty directory or glob; CI pipelines generating the topic list from a template that rendered zero entries; version drift between client and broker where an older client sends a different body schema so the list field never populates.","solutions":["On the client, guard before sending: skip the updateTopicList call (or fail fast) when the topic config collection is empty.","Verify the request body is a CreateTopicListRequestBody with a non-null 'topicConfigList' JSON key matching the broker's version.","If the list is legitimately empty, treat it as a no-op locally instead of issuing the RPC.","Check for null elements inside the list too — they trigger the sibling 'topic config is null.' error at the next check."],"exampleFix":"// before\nList<TopicConfig> configs = collectTopics();\nadminBrokerExt.updateTopicList(configs); // throws when configs is empty\n\n// after\nList<TopicConfig> configs = collectTopics();\nif (configs == null || configs.isEmpty()) {\n    return; // nothing to create, do not send the request\n}\nadminBrokerExt.updateTopicList(configs);","handlingStrategy":"validation","validationCode":"// before sending UPDATE_AND_CREATE_TOPIC_LIST\nif (CollectionUtils.isEmpty(topicConfigList)) {\n    return; // or throw IllegalArgumentException(\"no topics to create\");\n}\nrequestBody.setTopicConfigList(topicConfigList);","typeGuard":null,"tryCatchPattern":"try { admin.updateTopicList(requestBody); }\ncatch (AuthorizationException e) {\n    if (e.getMessage().contains(\"topic list is empty\")) { /* no-op, skip */ return; }\n    throw e;\n}","preventionTips":["Guard bulk admin calls with an empty-collection short-circuit","Log the list size before sending batch requests","Unit-test the serialization of request bodies against the broker's schema"],"tags":["rocketmq","authorization","admin-api","validation","topic"],"backgroundTag":null,"analyzedSha":"293f5885719fc4aa3619446a1900f58ccfcfdd29","analyzedAt":"2026-08-14T11:50:13.822Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}