{"record":{"id":"4b647778e719c71e","repo":"apache/rocketmq","slug":"topic-config-is-null","errorCode":null,"errorMessage":"topic config is null.","messagePattern":"topic config is null\\.","errorType":"exception","errorClass":"AuthorizationException","httpStatus":null,"severity":"error","filePath":"auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilder.java","lineNumber":471,"sourceCode":"                        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                    }\n                    Set<String> coldDataResources = new LinkedHashSet<>();\n                    for (String consumerGroup : properties.stringPropertyNames()) {\n                        addUniqueContext(result, coldDataResources, subject,\n                            Resource.ofGroup(requireResource(consumerGroup, \"consumer group\")),\n                            Action.UPDATE, sourceIp);","sourceCodeStart":453,"sourceCodeEnd":489,"githubUrl":"https://github.com/apache/rocketmq/blob/293f5885719fc4aa3619446a1900f58ccfcfdd29/auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilder.java#L453-L489","documentation":"Thrown while iterating the topicConfigList of an UPDATE_AND_CREATE_TOPIC_LIST request when an individual TopicConfig element is null. The builder dereferences each element (topicConfig.getTopicName()) to build a CREATE Resource, so a null slot in the list is rejected before any permission check runs. It is a payload-shape error: the list itself was non-empty but contains a null entry.","triggerScenarios":"A CreateTopicListRequestBody JSON like {\"topicConfigList\":[null,{\"topicName\":\"t\"}]} — e.g. a stream/mapping pipeline that inserts null for failed lookups, or a Java client doing new ArrayList<>(size) and setting only some indices, or Collections.nCopies(n, null).","commonSituations":"Bulk provisioning code that maps an input list to TopicConfig and leaves null on parse failure; JSON built by hand or by another language where trailing commas / missing entries decode to null; partial failures in a loop that adds null placeholders.","solutions":["Filter nulls out of the topic config list before serializing and sending the request.","Fix the producer of the list so parse/build failures throw instead of yielding null elements.","Log which index was null while building to find the bad source entry."],"exampleFix":"// before\nList<TopicConfig> list = topics.stream().map(this::parse).collect(toList());\nbody.setTopicConfigList(list); // may contain nulls\n\n// after\nList<TopicConfig> list = topics.stream().map(this::parse)\n    .filter(Objects::nonNull).collect(toList());\nif (list.isEmpty()) { return; }\nbody.setTopicConfigList(list);","handlingStrategy":"validation","validationCode":"boolean valid = topicConfigList != null && !topicConfigList.isEmpty()\n    && topicConfigList.stream().allMatch(Objects::nonNull);\nif (!valid) { throw new IllegalArgumentException(\"topic config list has null entries\"); }","typeGuard":null,"tryCatchPattern":"try { admin.updateTopicList(body); }\ncatch (AuthorizationException e) {\n    if (\"topic config is null.\".equals(e.getMessage())) { fixAndRetryWithoutNulls(); return; }\n    throw e;\n}","preventionTips":["Filter Objects::nonNull before serializing lists","Make mapping steps throw on failure instead of returning null"],"tags":["rocketmq","authorization","admin-api","null-safety","topic"],"backgroundTag":null,"analyzedSha":"293f5885719fc4aa3619446a1900f58ccfcfdd29","analyzedAt":"2026-08-14T11:50:13.822Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}