apache/pulsar · error · RestException
%s is a non-partitioned topic. Instead of calling delete-par
Error message
%s is a non-partitioned topic. Instead of calling delete-partitioned-topic please call delete.
What it means
HTTP 409 CONFLICT raised in internalDeletePartitionedTopic when the partitioned metadata reports partitions < 1 but the name actually resolves to an existing non-partitioned topic. The delete-partitioned-topic admin endpoint only applies to partitioned topics; calling it on a plain topic would be a mistake, so the broker conflicts and tells the caller to use the normal delete endpoint instead.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java:831
throw new RestException(Status.NOT_FOUND, getTopicNotFoundErrorMessage(topicName.toString()));
}
});
}
protected void internalDeletePartitionedTopic(AsyncResponse asyncResponse,
boolean authoritative,
boolean force) {
validateNamespaceOperationAsync(topicName.getNamespaceObject(), NamespaceOperation.DELETE_TOPIC)
.thenCompose(__ -> validateTopicOwnershipAsync(topicName, authoritative))
.thenCompose(__ -> pulsar().getBrokerService()
.fetchPartitionedTopicMetadataAsync(topicName)
.thenCompose(partitionedMeta -> {
final int numPartitions = partitionedMeta.partitions;
if (numPartitions < 1) {
return pulsar().getNamespaceService().checkNonPartitionedTopicExists(topicName)
.thenApply(exists -> {
if (exists) {
throw new RestException(Response.Status.CONFLICT,
String.format("%s is a non-partitioned topic. Instead of calling"
+ " delete-partitioned-topic please call delete.", topicName));
} else {
throw new RestException(Status.NOT_FOUND,
String.format("Topic %s not found.", topicName));
}
});
}
return internalRemovePartitionsAuthenticationPoliciesAsync()
.thenCompose(unused -> internalRemovePartitionsTopicAsync(numPartitions, force));
})
// Only tries to delete the znode for partitioned topic when all its partitions are successfully deleted
).thenCompose(ignore ->
pulsar().getBrokerService().deleteSchema(topicName).exceptionally(ex -> null)
).thenCompose(ignore ->
pulsar().getTopicPoliciesService().deleteTopicPoliciesAsync(topicName).exceptionally(ex -> null)
).thenCompose(__ -> getPulsarResources().getNamespaceResources().getPartitionedTopicResources()
.runWithMarkDeleteAsync(topicName, () -> namespaceResources()View on GitHub (pinned to 820761864e)
Solutions
- Use the plain delete endpoint instead: `pulsar-admin topics delete <topic>` (DELETE /admin/v2/persistent/{ns}/{topic}).
- Confirm the topic's partition count with `pulsar-admin topics get-partitioned-topic-metadata <topic>` before choosing the delete variant.
- Fix automation to branch on metadata.partitions > 0 and call the matching delete API.
- If a partitioned topic was intended, delete the non-partitioned topic first and recreate it with -p N.
Example fix
// before pulsar-admin topics delete-partitioned-topic my-tenant/my-ns/my-topic // 409 non-partitioned // after pulsar-admin topics delete my-tenant/my-ns/my-topic
Defensive patterns
Strategy: validation
Validate before calling
PartitionedTopicMetadata meta = admin.topics().getPartitionedTopicMetadata(topic);
if (meta.partitions > 0) {
admin.topics().deletePartitionedTopic(topic, force);
} else {
admin.topics().delete(topic, force);
} Try / catch
try {
admin.topics().deletePartitionedTopic(topic);
} catch (PulsarAdminException e) {
if (e.getStatusCode() == 409) {
admin.topics().delete(topic); // non-partitioned
} else throw e;
} Prevention
- Check partitioned metadata before choosing a delete endpoint
- Branch automation on partitions > 0
- Never assume all topics in a namespace are partitioned
When it happens
Trigger: DELETE /admin/v2/persistent/{ns}/{topic}/partitionedTopic (or `pulsar-admin topics delete-partitioned-topic`) on a topic created with `topics create` (non-partitioned), i.e. numPartitions is 0 while the topic itself exists.
Common situations: Script assumes every topic is partitioned; topic was converted/created non-partitioned by another tool; using delete-partitioned-topic to clean up plain topics; typo where a non-partitioned twin of the name exists.
Related errors
- Cannot delete non empty namespace
- cluster data is required
- Cluster already exists
- ${duplicateBrokers} already exists in ${domainName}
- Cannot delete non empty bundle
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/6b6723f3274e23c4.
Report an issue: GitHub.