apache/pulsar · warning · RestException
Get message ID by timestamp on a partitioned topic is not al
Error message
Get message ID by timestamp on a partitioned topic is not allowed, please try do it on specific topic partition
What it means
The 'get message ID by timestamp' admin operation rejects partitioned topics with a 405. Timestamp-to-position lookup requires scanning a single managed ledger; on a partitioned topic there are many ledgers and no single answer, so the caller is told to query a specific partition. The check is skipped entirely if the topic name is already a partition name.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java:3007
return String.format("Topic [%s] internal get message by id",
PersistentTopicsBase.this.topicName);
}
}, null);
return results;
});
}
protected CompletableFuture<MessageId> internalGetMessageIdByTimestampAsync(long timestamp, boolean authoritative) {
return validateTopicOperationAsync(topicName, TopicOperation.PEEK_MESSAGES)
.thenCompose(__ -> validateGlobalNamespaceOwnershipAsync(namespaceName))
.thenCompose(__ -> {
if (topicName.isPartitioned()) {
return CompletableFuture.completedFuture(null);
} else {
return getPartitionedTopicMetadataAsync(topicName, authoritative, false)
.thenAccept(metadata -> {
if (metadata.partitions > 0) {
throw new RestException(Status.METHOD_NOT_ALLOWED,
"Get message ID by timestamp on a partitioned topic is not allowed, "
+ "please try do it on specific topic partition");
}
});
}
}).thenCompose(__ -> validateTopicOwnershipAsync(topicName, authoritative))
.thenCompose(__ -> getTopicReferenceAsync(topicName))
.thenCompose(topic -> {
if (!(topic instanceof PersistentTopic)) {
log.error()
.attr("topic", topicName)
.log("Not supported operation of non-persistent topic");
throw new RestException(Status.METHOD_NOT_ALLOWED,
"Get message ID by timestamp on a non-persistent topic is not allowed");
}
final PersistentTopic persistentTopic = (PersistentTopic) topic;
final var compactionService = persistentTopic.getTopicCompactionService();
View on GitHub (pinned to 820761864e)
Solutions
- Call the endpoint on a concrete partition, e.g. my-topic-partition-2
- Iterate all partitions, collecting each partition's message ID at the timestamp
- If the goal is seek-to-time on a consumer, use cursor reset by timestamp instead, which works on partitioned topics
Example fix
// before
String msgId = admin.topics().getMessageIdByTimestamp("persistent://public/default/my-topic", ts);
// after
String msgId = admin.topics().getMessageIdByTimestamp("persistent://public/default/my-topic-partition-0", ts); Defensive patterns
Strategy: validation
Validate before calling
PartitionedTopicMetadata md = admin.topics().getPartitionedMetadata(topic);
if (md.partitions > 0) { /* query each md.partitions partition instead */ } Type guard
boolean isPartitionName(String topic) { return topic.matches(".*-partition-\\d+$"); } Prevention
- Use cursor reset-by-timestamp when you need partitioned-topic time seek
- Fan out timestamp lookups across partitions and merge results
When it happens
Trigger: GET /admin/v2/persistent/{tenant}/{namespace}/{topic}/messageid/{timestamp} (or getMessagedIdByTimestamp) with a partitioned topic base name and partitions > 0.
Common situations: Time-travel/datetime-seek tooling resolving a topic alias; users migrating from timestamp-based cursor reset (which supports partitioned topics) to the messageid-by-timestamp lookup assuming identical semantics.
Related errors
- Reset-cursor at position is not allowed for partitioned-topi
- GetMessageById is not allowed on partitioned-topic
- Peek messages on a partitioned topic is not allowed
- Examine messages on a partitioned topic is not allowed, plea
- Get message ID by timestamp on a non-persistent topic is not
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/c97a07e7855eb28c.
Report an issue: GitHub.