apache/pulsar · warning · RestException
Peek messages on a partitioned topic is not allowed
Error message
Peek messages on a partitioned topic is not allowed
What it means
Peek/preview of messages (peekNthMessage via the admin REST examine/peek endpoints) is only valid on an individual partition, since entries live in that partition's managed ledger. For a partitioned topic base name with partitions > 0 the broker throws 405 METHOD_NOT_ALLOWED 'Peek messages on a partitioned topic is not allowed'. The check is skipped when the name already refers to a partition.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java:3074
if (position == null) {
return null;
} else {
return new MessageIdImpl(position.getLedgerId(), position.getEntryId(),
topicName.getPartitionIndex());
}
});
}
protected CompletableFuture<Response> internalPeekNthMessageAsync(String subName, int messagePosition,
boolean authoritative) {
return validateTopicOperationAsync(topicName, TopicOperation.PEEK_MESSAGES, subName)
.thenCompose(__ -> {
// If the topic name is a partition name, no need to get partition topic metadata again
if (!topicName.isPartitioned()) {
return getPartitionedTopicMetadataAsync(topicName, authoritative, false)
.thenCompose(topicMetadata -> {
if (topicMetadata.partitions > 0) {
throw new RestException(Status.METHOD_NOT_ALLOWED,
"Peek messages on a partitioned topic is not allowed");
}
return CompletableFuture.completedFuture(null);
});
} else {
return CompletableFuture.completedFuture(null);
}
}).thenCompose(__ -> validateTopicOwnershipAsync(topicName, authoritative))
.thenCompose(__ -> getTopicReferenceAsync(topicName))
.thenCompose(topic -> {
CompletableFuture<Entry> entry;
if (!(topic instanceof PersistentTopic)) {
log.error()
.attr("topic", topicName)
.attr("subscription", subName)
.log("Not supported operation of non-persistent topic");
throw new RestException(Status.METHOD_NOT_ALLOWED,
"Peek messages on a non-persistent topic is not allowed");View on GitHub (pinned to 820761864e)
Solutions
- Peek on a concrete partition: replace my-topic with my-topic-partition-N
- Enumerate partitions first via the partitions endpoint and peek each as needed
- Use the consumer-level receive/peek client API if you need cross-partition message visibility
Example fix
// before
Messages msg = admin.topics().peekMessages("persistent://public/default/my-topic", "my-sub", 1);
// after
Messages msg = admin.topics().peekMessages("persistent://public/default/my-topic-partition-0", "my-sub", 1); Defensive patterns
Strategy: validation
Validate before calling
PartitionedTopicMetadata md = admin.topics().getPartitionedMetadata(topic);
if (md.partitions > 0) throw new IllegalArgumentException("Peek a specific partition"); Type guard
boolean isPartitionName(String topic) { return topic.matches(".*-partition-\\d+$"); } Try / catch
try { admin.topics().peekMessages(topic, sub, n); }
catch (PulsarAdminException e) { if (e.getStatusCode() == 405) { /* use partitions */ } } Prevention
- Resolve partitions before per-partition admin operations
- Cache partition lists and refresh on NotFound errors
When it happens
Trigger: GET /admin/v2/persistent/{tenant}/{namespace}/{topic}/subscription/{sub}/position/{messagePosition} (peek) with a partitioned topic base name; also internal peekNthMessage paths after the metadata check.
Common situations: Debugging subscriptions on a partitioned topic and forgetting that peek needs the -partition-N suffix; admin consoles listing topics by base name.
Related errors
- Reset-cursor at position is not allowed for partitioned-topi
- GetMessageById is not allowed on partitioned-topic
- Get message ID by timestamp on a partitioned topic is not al
- 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/bc2b2b02a62e0b6d.
Report an issue: GitHub.