apache/rocketmq · error · AuthorizationException

cold data flow config is empty.

Error message

cold data flow config is empty.

What it means

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.

Solutions

  1. Send the body as standard properties text: one 'consumerGroup=anything' line per group, newline separated.
  2. Confirm the client version's body format matches the broker's MixAll.string2Properties expectation (java.util.Properties.load semantics).
  3. Test-serialize the properties string locally and assert Properties#stringPropertyNames() is non-empty before sending.

Example fix

// before
String body = String.join(",", groups); // unparseable as properties
command.setBody(body.getBytes(UTF_8));

// after
StringBuilder sb = new StringBuilder();
for (String g : groups) { sb.append(g).append("=1\n"); }
if (sb.length() == 0) { return; }
command.setBody(sb.toString().getBytes(UTF_8));
Defensive patterns

Strategy: validation

Validate before calling

String text = toPropertiesText(groups);
Properties check = MixAll.string2Properties(text);
if (check == null || check.isEmpty()) { throw new IllegalArgumentException("bad cold data config text"); }

Try / catch

try { broker.updateColdDataFlowCtrConfig(text); }
catch (AuthorizationException e) {
    if (e.getMessage().contains("cold data flow config is empty")) { rebuildBodyAsProperties(); return; }
    throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of apache/rocketmq@293f588571 (2026-08-14). Data as JSON: /api/errors/97189646c8181ef3. Report an issue: GitHub.

Appendix: source

Thrown at auth/src/main/java/org/apache/rocketmq/auth/authorization/builder/DefaultAuthorizationContextBuilder.java:483

                    if (CollectionUtils.isEmpty(topicListBody.getTopicConfigList())) {
                        throw new AuthorizationException("topic list is empty.");
                    }
                    Set<String> topicListResources = new LinkedHashSet<>();
                    for (TopicConfig topicConfig : topicListBody.getTopicConfigList()) {
                        if (topicConfig == null) {
                            throw new AuthorizationException("topic config is null.");
                        }
                        String topicName = requireResource(topicConfig.getTopicName(), "topic");
                        Resource resource = NamespaceUtil.isRetryTopic(topicName)
                            ? Resource.ofGroup(topicName) : Resource.ofTopic(topicName);
                        addUniqueContext(result, topicListResources, subject, resource, Action.CREATE, sourceIp);
                    }
                    break;
                case RequestCode.UPDATE_COLD_DATA_FLOW_CTR_CONFIG:
                    Properties properties = MixAll.string2Properties(
                        decodeRequiredText(command, "cold data flow config"));
                    if (properties == null || properties.isEmpty()) {
                        throw new AuthorizationException("cold data flow config is empty.");
                    }
                    Set<String> coldDataResources = new LinkedHashSet<>();
                    for (String consumerGroup : properties.stringPropertyNames()) {
                        addUniqueContext(result, coldDataResources, subject,
                            Resource.ofGroup(requireResource(consumerGroup, "consumer group")),
                            Action.UPDATE, sourceIp);
                    }
                    break;
                case RequestCode.REMOVE_COLD_DATA_FLOW_CTR_CONFIG:
                    group = Resource.ofGroup(requireResource(
                        decodeRequiredText(command, "consumer group"), "consumer group"));
                    result.add(DefaultAuthorizationContext.of(subject, group, Action.UPDATE, sourceIp));
                    break;
                case RequestCode.UPDATE_AND_CREATE_SUBSCRIPTIONGROUP:
                    SubscriptionGroupConfig subscriptionGroupConfig =
                        RemotingSerializable.decode(command.getBody(), SubscriptionGroupConfig.class);
                    if (subscriptionGroupConfig == null
                        || StringUtils.isBlank(subscriptionGroupConfig.getGroupName())) {

View on GitHub (pinned to 293f588571)