{"record":{"id":"97189646c8181ef3","repo":"apache/rocketmq","slug":"cold-data-flow-config-is-empty","errorCode":null,"errorMessage":"cold data flow config is empty.","messagePattern":"cold data flow config is empty\\.","errorType":"exception","errorClass":"AuthorizationException","httpStatus":null,"severity":"error","filePath":"auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilder.java","lineNumber":483,"sourceCode":"                    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);\n                    }\n                    break;\n                case RequestCode.REMOVE_COLD_DATA_FLOW_CTR_CONFIG:\n                    group = Resource.ofGroup(requireResource(\n                        decodeRequiredText(command, \"consumer group\"), \"consumer group\"));\n                    result.add(DefaultAuthorizationContext.of(subject, group, Action.UPDATE, sourceIp));\n                    break;\n                case RequestCode.UPDATE_AND_CREATE_SUBSCRIPTIONGROUP:\n                    SubscriptionGroupConfig subscriptionGroupConfig =\n                        RemotingSerializable.decode(command.getBody(), SubscriptionGroupConfig.class);\n                    if (subscriptionGroupConfig == null\n                        || StringUtils.isBlank(subscriptionGroupConfig.getGroupName())) {","sourceCodeStart":465,"sourceCodeEnd":501,"githubUrl":"https://github.com/apache/rocketmq/blob/293f5885719fc4aa3619446a1900f58ccfcfdd29/auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilder.java#L465-L501","documentation":"Thrown when handling UPDATE_COLD_DATA_FLOW_CTR_CONFIG: the request body is decoded as text and parsed into java.util.Properties via MixAll.string2Properties, and the result is null (unparseable) or has no keys. Because each property name becomes a consumer-group Resource to authorize an UPDATE against, an empty property set cannot produce any authorization context and the request fails with AuthorizationException.","triggerScenarios":"Sending RequestCode.UPDATE_COLD_DATA_FLOW_CTR_CONFIG whose body text is empty after trim, is not valid key=value properties syntax (string2Properties returns null), or is a properties string with zero entries. The earlier decodeRequiredText already guarantees the body bytes are non-empty, so this fires on 'non-empty but unparseable/empty-as-properties' input.","commonSituations":"Body encoded as JSON or a bare group name instead of 'key=value' lines; properties string built with String.join(\",\") instead of newlines; double-encoding (whole properties object toString'd) after a client upgrade changed the body format.","solutions":["Send the body as standard properties text: one 'consumerGroup=anything' line per group, newline separated.","Confirm the client version's body format matches the broker's MixAll.string2Properties expectation (java.util.Properties.load semantics).","Test-serialize the properties string locally and assert Properties#stringPropertyNames() is non-empty before sending."],"exampleFix":"// before\nString body = String.join(\",\", groups); // unparseable as properties\ncommand.setBody(body.getBytes(UTF_8));\n\n// after\nStringBuilder sb = new StringBuilder();\nfor (String g : groups) { sb.append(g).append(\"=1\\n\"); }\nif (sb.length() == 0) { return; }\ncommand.setBody(sb.toString().getBytes(UTF_8));","handlingStrategy":"validation","validationCode":"String text = toPropertiesText(groups);\nProperties check = MixAll.string2Properties(text);\nif (check == null || check.isEmpty()) { throw new IllegalArgumentException(\"bad cold data config text\"); }","typeGuard":null,"tryCatchPattern":"try { broker.updateColdDataFlowCtrConfig(text); }\ncatch (AuthorizationException e) {\n    if (e.getMessage().contains(\"cold data flow config is empty\")) { rebuildBodyAsProperties(); return; }\n    throw e;\n}","preventionTips":["Encode cold-data bodies as newline-separated key=value properties","Round-trip parse the properties string locally before sending"],"tags":["rocketmq","authorization","cold-data","properties","validation"],"backgroundTag":null,"analyzedSha":"293f5885719fc4aa3619446a1900f58ccfcfdd29","analyzedAt":"2026-08-14T11:50:13.822Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}