apache/rocketmq · error · AuthorizationException

topic config is null.

Error message

topic config is null.

What it means

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.

Solutions

  1. Filter nulls out of the topic config list before serializing and sending the request.
  2. Fix the producer of the list so parse/build failures throw instead of yielding null elements.
  3. Log which index was null while building to find the bad source entry.

Example fix

// before
List<TopicConfig> list = topics.stream().map(this::parse).collect(toList());
body.setTopicConfigList(list); // may contain nulls

// after
List<TopicConfig> list = topics.stream().map(this::parse)
    .filter(Objects::nonNull).collect(toList());
if (list.isEmpty()) { return; }
body.setTopicConfigList(list);
Defensive patterns

Strategy: validation

Validate before calling

boolean valid = topicConfigList != null && !topicConfigList.isEmpty()
    && topicConfigList.stream().allMatch(Objects::nonNull);
if (!valid) { throw new IllegalArgumentException("topic config list has null entries"); }

Try / catch

try { admin.updateTopicList(body); }
catch (AuthorizationException e) {
    if ("topic config is null.".equals(e.getMessage())) { fixAndRetryWithoutNulls(); return; }
    throw e;
}

Prevention

When it happens

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

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

Related errors


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

Appendix: source

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

                        addUniqueContext(result, liteSubscriptionResources, subject,
                            Resource.ofTopic(requireResource(subscription.getTopic(), "topic")),
                            Action.SUB, sourceIp);
                    }
                    break;
                case RequestCode.UPDATE_BROKER_CONFIG:
                    result.add(DefaultAuthorizationContext.of(subject,
                        Resource.ofCluster(authConfig.getClusterName()), Action.UPDATE, sourceIp));
                    break;
                case RequestCode.UPDATE_AND_CREATE_TOPIC_LIST:
                    CreateTopicListRequestBody topicListBody = decodeRequiredBody(
                        command, CreateTopicListRequestBody.class, "topic list");
                    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);

View on GitHub (pinned to 293f588571)