apache/rocketmq · error · RuntimeException
Can't accept data with unmatched scope %s != %s
Error message
Can't accept data with unmatched scope %s != %s
What it means
Non-force update of topic-queue mapping requires the new detail's scope to equal the stored one; a different scope (the namespace/domain a mapping belongs to) is rejected with RuntimeException. Scope is part of the mapping identity and must stay stable across updates.
Source
Thrown at broker/src/main/java/org/apache/rocketmq/broker/topic/TopicQueueMappingManager.java:109
topicQueueMappingTable.put(newDetail.getTopic(), newDetail);
updated = true;
return;
}
if (force) {
//bakeup the old items
oldDetail.getHostedQueues().forEach((queueId, items) -> {
newDetail.getHostedQueues().putIfAbsent(queueId, items);
});
topicQueueMappingTable.put(newDetail.getTopic(), newDetail);
updated = true;
return;
}
//do more check
if (newDetail.getEpoch() < oldDetail.getEpoch()) {
throw new RuntimeException(String.format("Can't accept data with small epoch %d < %d", newDetail.getEpoch(), oldDetail.getEpoch()));
}
if (!newDetail.getScope().equals(oldDetail.getScope())) {
throw new RuntimeException(String.format("Can't accept data with unmatched scope %s != %s", newDetail.getScope(), oldDetail.getScope()));
}
boolean epochEqual = newDetail.getEpoch() == oldDetail.getEpoch();
for (Integer globalId : oldDetail.getHostedQueues().keySet()) {
List<LogicQueueMappingItem> oldItems = oldDetail.getHostedQueues().get(globalId);
List<LogicQueueMappingItem> newItems = newDetail.getHostedQueues().get(globalId);
if (newItems == null) {
if (epochEqual) {
throw new RuntimeException("Cannot accept equal epoch with null data");
} else {
newDetail.getHostedQueues().put(globalId, oldItems);
}
} else {
TopicQueueMappingUtils.makeSureLogicQueueMappingItemImmutable(oldItems, newItems, epochEqual, isClean);
}
}
topicQueueMappingTable.put(newDetail.getTopic(), newDetail);
updated = true;
} finally {View on GitHub (pinned to 293f588571)
Solutions
- Read the existing mapping's scope and submit the update with the identical scope value.
- Standardize which scope (global vs cluster/domain) manages this topic; don't interleave commands with different scope settings.
- If a genuine scope migration is needed, do it as a controlled migration (force path / recreate mapping), not an in-place update.
Defensive patterns
Strategy: validation
Validate before calling
TopicQueueMappingDetail old = manager.queryTopicQueueMapping(topic);
if (old != null && !newDetail.getScope().equals(old.getScope())) {
newDetail.setScope(old.getScope()); // scope is immutable across updates
} Type guard
boolean scopeMatches(TopicQueueMappingDetail oldD, TopicQueueMappingDetail newD) {
return oldD == null || oldD.getScope().equals(newD.getScope());
} Try / catch
catch (RuntimeException e) { if (e.getMessage().contains("unmatched scope")) { adoptStoredScope(); resubmit(); } else throw e; } Prevention
- Never mix global-scope and cluster-scope mapping commands for the same topic.
- Pin one scope convention per environment in tooling.
When it happens
Trigger: updateTopicQueueMapping submitting detail with scope 'clusterA' while stored detail has scope 'domainA' (or full vs partial scope names) with equal-or-greater epoch.
Common situations: Mixing global-scope and cluster-scope mapping commands; tools upgraded between versions that change default scope strings; copying mapping config across environments with different scope configuration.
Related errors
- Can't accept data with small epoch %d < %d
- Cannot accept equal epoch with null data
- Consumer group is not allowed to consume.
- datetime is null.
- User:{} is not found.
AI-assisted analysis of apache/rocketmq@293f588571 (2026-08-14).
Data as JSON: /api/errors/a4ba14db44f650b8.
Report an issue: GitHub.