{"record":{"id":"bc2b2b02a62e0b6d","repo":"apache/pulsar","slug":"peek-messages-on-a-partitioned-topic-is-not-allowe","errorCode":null,"errorMessage":"Peek messages on a partitioned topic is not allowed","messagePattern":"Peek messages on a partitioned topic is not allowed","errorType":"http","errorClass":"RestException","httpStatus":405,"severity":"warning","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java","lineNumber":3074,"sourceCode":"            if (position == null) {\n                return null;\n            } else {\n                return new MessageIdImpl(position.getLedgerId(), position.getEntryId(),\n                    topicName.getPartitionIndex());\n            }\n        });\n    }\n\n    protected CompletableFuture<Response> internalPeekNthMessageAsync(String subName, int messagePosition,\n                                                                      boolean authoritative) {\n        return validateTopicOperationAsync(topicName, TopicOperation.PEEK_MESSAGES, subName)\n        .thenCompose(__ -> {\n            // If the topic name is a partition name, no need to get partition topic metadata again\n            if (!topicName.isPartitioned()) {\n                return getPartitionedTopicMetadataAsync(topicName, authoritative, false)\n                        .thenCompose(topicMetadata -> {\n                            if (topicMetadata.partitions > 0) {\n                                throw new RestException(Status.METHOD_NOT_ALLOWED,\n                                        \"Peek messages on a partitioned topic is not allowed\");\n                            }\n                            return CompletableFuture.completedFuture(null);\n                        });\n            } else {\n                return CompletableFuture.completedFuture(null);\n            }\n        }).thenCompose(__ -> validateTopicOwnershipAsync(topicName, authoritative))\n        .thenCompose(__ -> getTopicReferenceAsync(topicName))\n        .thenCompose(topic -> {\n            CompletableFuture<Entry> entry;\n            if (!(topic instanceof PersistentTopic)) {\n                log.error()\n                        .attr(\"topic\", topicName)\n                        .attr(\"subscription\", subName)\n                        .log(\"Not supported operation of non-persistent topic\");\n                throw new RestException(Status.METHOD_NOT_ALLOWED,\n                        \"Peek messages on a non-persistent topic is not allowed\");","sourceCodeStart":3056,"sourceCodeEnd":3092,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java#L3056-L3092","documentation":"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.","triggerScenarios":"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.","commonSituations":"Debugging subscriptions on a partitioned topic and forgetting that peek needs the -partition-N suffix; admin consoles listing topics by base name.","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"],"exampleFix":"// before\nMessages msg = admin.topics().peekMessages(\"persistent://public/default/my-topic\", \"my-sub\", 1);\n// after\nMessages msg = admin.topics().peekMessages(\"persistent://public/default/my-topic-partition-0\", \"my-sub\", 1);","handlingStrategy":"validation","validationCode":"PartitionedTopicMetadata md = admin.topics().getPartitionedMetadata(topic);\nif (md.partitions > 0) throw new IllegalArgumentException(\"Peek a specific partition\");","typeGuard":"boolean isPartitionName(String topic) { return topic.matches(\".*-partition-\\\\d+$\"); }","tryCatchPattern":"try { admin.topics().peekMessages(topic, sub, n); }\ncatch (PulsarAdminException e) { if (e.getStatusCode() == 405) { /* use partitions */ } }","preventionTips":["Resolve partitions before per-partition admin operations","Cache partition lists and refresh on NotFound errors"],"tags":["rest-api","partitioned-topic","method-not-allowed","admin"],"backgroundTag":"operation-not-supported-on-partitioned-topic","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}