apache/pulsar · error · org.apache.pulsar.broker.admin.RestException

Topic is not partitioned topic

Error message

Topic is not partitioned topic

What it means

Returned by validatePartitionedTopicMetadataAsync when the addressed topic exists in metadata but has partitions < 1 — i.e. it is a non-partitioned topic (or a partitioned topic with zero partitions). Endpoints that only operate on partitioned topics (e.g. partition-level admin ops like getPartitions, update-partitions) reject the request with HTTP 409 Conflict.

Source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/AdminResource.java:295

            throw new RestException(Status.NOT_ACCEPTABLE, "Need to provide a persistent topic name");
        }
    }

    protected void validatePartitionedTopicName(String tenant, String namespace, String encodedTopic) {
        // first, it has to be a validate topic name
        validateTopicName(tenant, namespace, encodedTopic);
        // second, "-partition-" is not allowed
        if (encodedTopic.contains(TopicName.PARTITIONED_TOPIC_SUFFIX)) {
            throw new RestException(Status.PRECONDITION_FAILED,
                    "Partitioned Topic Name should not contain '-partition-'");
        }
    }

    protected CompletableFuture<Void> validatePartitionedTopicMetadataAsync() {
        return pulsar().getBrokerService().fetchPartitionedTopicMetadataAsync(topicName)
                .thenAccept(metadata -> {
                    if (metadata.partitions < 1) {
                        throw new RestException(Status.CONFLICT, "Topic is not partitioned topic");
                    }
                });
    }

    protected WorkerService validateAndGetWorkerService() {
        try {
            return pulsar().getWorkerService();
        } catch (UnsupportedOperationException e) {
            throw new RestException(Status.CONFLICT, e.getMessage());
        }
    }

    /**
     * @deprecated Use {@link #getNamespacePoliciesAsync(NamespaceName)} instead.
     */
    @Deprecated
    protected Policies getNamespacePolicies(NamespaceName namespaceName) {
        try {

View on GitHub (pinned to 820761864e)

Solutions

  1. Check the topic's metadata first (GET /admin/v2/persistent/tenant/ns/topic/partitions or namespaces getPartitionedTopics list) and only call partitioned APIs on partitioned topics.
  2. If the topic should be partitioned, delete the non-partitioned topic and create it with the desired number of partitions (or use updatePartitionedTopic if it exists with partitions).
  3. Update calling code to branch between partitioned and non-partitioned handling using the topic's metadata.
  4. Use the admin client's topics().getPartitionedTopicMetadata(...) which handles this check gracefully.

Example fix

// before
admin.topics().getPartitionedTopicMetadata("tenant/ns/orders"); // 409 if not partitioned
// after
PartitionedTopicMetadata md = admin.topics().getPartitionedTopicMetadata("tenant/ns/orders");
if (md.partitions < 1) {
    admin.topics().createPartitionedTopic("tenant/ns/orders", 4);
}
Defensive patterns

Strategy: type-guard

Validate before calling

// Java: check partition metadata before partitioned-only calls
PartitionedTopicMetadata md = admin.topics().getPartitionedTopicMetadata(fqTopic);
if (md.partitions >= 1) { /* safe to call partitioned APIs */ }

Type guard

boolean isPartitioned(PartitionedTopicMetadata md) { return md != null && md.partitions >= 1; }

Try / catch

try {
    admin.topics().getPartitionedInfo(fqTopic);
} catch (PulsarAdminException e) {
    if (e.getStatusCode() == 409) {
        // not partitioned: fall back to non-partitioned handling
    }
}

Prevention

When it happens

Trigger: Calling partitioned-only admin endpoints (GET .../partitions, POST .../updatePartitions, DELETE partitioned topic) against a topic created as a plain non-partitioned topic, or after partitions were set to 0.

Common situations: Assuming every topic is partitioned in shared tooling; a topic was re-created as non-partitioned after an earlier cleanup; code paths that must handle both topic kinds but only guard one.

Related errors


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