apache/pulsar · warning · RestException
Examine messages on a partitioned topic is not allowed, plea
Error message
Examine messages on a partitioned topic is not allowed, please try examine message on specific topic partition
What it means
The 'examine messages' admin operation (verifies a topic's latest messages can be read) does not work on a partitioned topic's base name; examination applies per partition. If the topic name is not already a partition and metadata shows partitions > 0, the broker throws 405 METHOD_NOT_ALLOWED with this message.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java:3139
entry.release();
}
}
});
}
protected CompletableFuture<Response> internalExamineMessageAsync(String initialPosition, long messagePosition,
boolean authoritative) {
long messagePositionLocal = messagePosition < 1 ? 1 : messagePosition;
String initialPositionLocal = initialPosition == null ? "latest" : initialPosition;
return validateTopicOperationAsync(topicName, TopicOperation.PEEK_MESSAGES)
.thenCompose(__ -> validateGlobalNamespaceOwnershipAsync(namespaceName))
.thenCompose(__ -> validateTopicOwnershipAsync(topicName, authoritative))
.thenCompose(__ -> {
if (!topicName.isPartitioned()) {
return getPartitionedTopicMetadataAsync(topicName, authoritative, false)
.thenCompose(partitionedTopicMetadata -> {
if (partitionedTopicMetadata.partitions > 0) {
throw new RestException(Status.METHOD_NOT_ALLOWED,
"Examine messages on a partitioned topic is not allowed, "
+ "please try examine message on specific topic partition");
} else {
return CompletableFuture.completedFuture(null);
}
});
}
return CompletableFuture.completedFuture(null);
}).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,
"Examine messages on a non-persistent topic is not allowed");
}
try {View on GitHub (pinned to 820761864e)
Solutions
- Run examine on each concrete partition (my-topic-partition-N)
- Enumerate partitions via the partitions endpoint and loop examine calls per partition
- Skip partitioned base names in bulk examine tooling, or expand them to partitions first
Example fix
// before
admin.topics().examineMessages("persistent://public/default/my-topic");
// after
for (String part : admin.topics().getPartitionedPartitions("persistent://public/default/my-topic")) {
admin.topics().examineMessages(part);
} Defensive patterns
Strategy: validation
Validate before calling
PartitionedTopicMetadata md = admin.topics().getPartitionedMetadata(topic);
if (md.partitions > 0) { /* examine each partition instead */ } Type guard
boolean isPartitionName(String topic) { return topic.matches(".*-partition-\\d+$"); } Prevention
- Expand partitioned base names into partitions in bulk-examine tooling
- Catch 405 and fall back to per-partition examination
When it happens
Trigger: GET /admin/v2/persistent/{tenant}/{namespace}/{topic}/examinemessages (or examineMessages call) with a partitioned topic base name instead of my-topic-partition-N.
Common situations: CI health checks that enumerate partitioned topics by base name; operators running examine across a namespace without resolving partitions.
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
- Peek messages on a partitioned topic is not allowed
- 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/f6aeadab307c762d.
Report an issue: GitHub.