apache/rocketmq · error · RuntimeException
Cannot accept equal epoch with null data
Error message
Cannot accept equal epoch with null data
What it means
Non-force update with epoch EQUAL to the stored one must contain data for every global queue id the old mapping hosted; if newItems is null for an existing globalId while epochs are equal, it throws RuntimeException. Equal-epoch updates are treated as re-submissions and must be complete, whereas a greater epoch may omit ids (old items are inherited).
Source
Thrown at broker/src/main/java/org/apache/rocketmq/broker/topic/TopicQueueMappingManager.java:117
});
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 {
if (locked) {
this.lock.unlock();
}
if (updated && flush) {
this.dataVersion.nextVersion();
this.persist();
log.info("Update topic queue mapping from [{}] to [{}], force {}", oldDetail, newDetail, force);
}View on GitHub (pinned to 293f588571)
Solutions
- Bump the epoch when the queue set genuinely changes, so omitted ids are inherited rather than rejected.
- If it is a pure retry, resend the COMPLETE mapping (all hosted queues) with the same epoch.
- Fix tooling that nulls-out empty or unchanged queue lists before submission.
Example fix
// before: same epoch, queue 1 missing -> rejected
newDetail.setEpoch(5); hostedQueues: {0: [...]} // old had {0:[...], 1:[...]}
// after: bump epoch for a real change
newDetail.setEpoch(6); hostedQueues: {0: [...]} // queue 1 inherited from old detail Defensive patterns
Strategy: validation
Validate before calling
TopicQueueMappingDetail old = manager.queryTopicQueueMapping(topic);
boolean epochEqual = old != null && newDetail.getEpoch() == old.getEpoch();
if (epochEqual) {
for (Integer id : old.getHostedQueues().keySet()) {
if (!newDetail.getHostedQueues().containsKey(id)) {
newDetail.getHostedQueues().put(id, old.getHostedQueues().get(id)); // complete the detail
}
}
} Type guard
boolean isCompleteForEqualEpoch(TopicQueueMappingDetail oldD, TopicQueueMappingDetail newD) {
return oldD == null || newD.getEpoch() > oldD.getEpoch()
|| newD.getHostedQueues().keySet().containsAll(oldD.getHostedQueues().keySet());
} Try / catch
catch (RuntimeException e) { if (e.getMessage().contains("equal epoch with null data")) { completeMissingQueuesFromCurrent(); resubmit(); } else throw e; } Prevention
- Bump the epoch whenever the hosted queue set changes.
- Same-epoch retries must resend the full mapping, every queue id included.
When it happens
Trigger: Retransmitting a mapping detail with the same epoch but with some hosted-queues entries removed/null; partial mapping JSON built by a tool dropping queues.
Common situations: Client retries an update after trimming the mapping; serialization dropping empty lists to null; hand-edited mapping files missing queue entries.
Related errors
- Can't accept data with small epoch %d < %d
- Can't accept data with unmatched scope %s != %s
- 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/c072c9d32d666ebf.
Report an issue: GitHub.