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

  1. Call the endpoint on a concrete partition, e.g. my-topic-partition-2
  2. Iterate all partitions, collecting each partition's message ID at the timestamp
  3. 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

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


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/c97a07e7855eb28c. Report an issue: GitHub.