apache/rocketmq · error · AuthorizationException

mapping topic does not match topic header.

Error message

mapping topic does not match topic header.

What it means

Thrown for UPDATE_AND_CREATE_STATIC_TOPIC when the topic in the CreateTopicRequestHeader does not equal the topic inside the TopicQueueMappingDetail body. Both are required resources and the builder cross-checks them (StringUtils.equals) so a client cannot get a CREATE authorized for topic A while actually registering mapping data for topic B; a mismatch is treated as a malformed or suspicious request.

Source

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

                            throw new AuthorizationException("subscription group config is null.");
                        }
                        addUniqueContext(result, subscriptionGroupResources, subject,
                            Resource.ofGroup(requireResource(groupConfig.getGroupName(), "consumer group")),
                            Action.CREATE, sourceIp);
                    }
                    break;
                case RequestCode.UPDATE_AND_CREATE_STATIC_TOPIC:
                    CreateTopicRequestHeader createTopicRequestHeader =
                        command.decodeCommandCustomHeader(CreateTopicRequestHeader.class);
                    if (createTopicRequestHeader == null) {
                        throw new AuthorizationException("topic header is null.");
                    }
                    String staticTopic = requireResource(createTopicRequestHeader.getTopic(), "topic");
                    TopicQueueMappingDetail mappingDetail = decodeRequiredBody(
                        command, TopicQueueMappingDetail.class, "topic queue mapping");
                    if (!StringUtils.equals(
                        staticTopic, requireResource(mappingDetail.getTopic(), "mapping topic"))) {
                        throw new AuthorizationException("mapping topic does not match topic header.");
                    }
                    topic = Resource.ofTopic(staticTopic);
                    result.add(DefaultAuthorizationContext.of(subject, topic, Action.CREATE, sourceIp));
                    break;
                case RequestCode.GET_BROKER_CONFIG:
                case RequestCode.GET_BROKER_RUNTIME_INFO:
                case RequestCode.GET_ALL_CONSUMER_OFFSET:
                case RequestCode.GET_TIMER_CHECK_POINT:
                case RequestCode.GET_ALL_DELAY_OFFSET:
                case RequestCode.GET_BROKER_HA_STATUS:
                case RequestCode.GET_BROKER_EPOCH_CACHE:
                case RequestCode.GET_BROKER_LITE_INFO:
                    result.add(DefaultAuthorizationContext.of(subject,
                        Resource.ofCluster(authConfig.getClusterName()), Action.GET, sourceIp));
                    break;
                case RequestCode.GET_ALL_TOPIC_CONFIG:
                case RequestCode.GET_TIMER_METRICS:
                case RequestCode.GET_SYSTEM_TOPIC_LIST_FROM_BROKER:

View on GitHub (pinned to 293f588571)

Solutions

  1. Set mappingDetail.setTopic(headerTopic) (or derive both from the same variable) before serializing the body.
  2. Add an assertion StringUtils.equals(header.getTopic(), mappingDetail.getTopic()) before sending.
  3. In bulk loops, construct a fresh TopicQueueMappingDetail per topic instead of mutating a shared instance.

Example fix

// before
CreateTopicRequestHeader h = newHeader(topicA);
TopicQueueMappingDetail d = templateDetail; // still holds topicB

// after
CreateTopicRequestHeader h = newHeader(topicA);
TopicQueueMappingDetail d = templateDeepCopy();
d.setTopic(h.getTopic());
Defensive patterns

Strategy: validation

Validate before calling

if (!StringUtils.equals(header.getTopic(), mappingDetail.getTopic())) {
    throw new IllegalArgumentException("mapping topic must equal header topic");
}

Try / catch

try { admin.createStaticTopic(header, mappingDetail); }
catch (AuthorizationException e) {
    if (e.getMessage().contains("does not match")) { mappingDetail.setTopic(header.getTopic()); resend(); return; }
    throw e;
}

Prevention

When it happens

Trigger: Header topic 'foo' but body {"topic":"bar",...} in TopicQueueMappingDetail JSON. Happens when the header and the mapping detail are built from different variables (copy-paste in scripts, stale cached mapping detail reused across topics, or a loop variable bug in bulk static-topic creation).

Common situations: Bulk creation of static topics reusing a template TopicQueueMappingDetail without resetting its topic field; concurrent code mutating a shared mapping object; retry logic that pairs a new header with an old body.

Related errors


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