apache/pulsar · error · RestException

Termination of a partitioned topic is not allowed

Error message

Termination of a partitioned topic is not allowed

What it means

internalTerminateAsync rejects termination of a partitioned topic (405): terminate must be called on individual partitions, since a partitioned topic is a metadata-only construct with no ledger of its own to truncate.

Solutions

  1. Terminate each partition individually
  2. Consider deleting the partitioned topic if the goal is full teardown
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java:3993 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/PersistentTopicsBase.java:3993

    protected CompletableFuture<Void> internalRemoveMaxConsumers(boolean isGlobal) {
        return pulsar().getTopicPoliciesService().updateTopicPoliciesAsync(topicName, isGlobal, true, policies -> {
            policies.setMaxConsumerPerTopic(null);
        });
    }

    protected CompletableFuture<MessageId> internalTerminateAsync(boolean authoritative) {
        if (SystemTopicNames.isSystemTopic(topicName)) {
            return FutureUtil.failedFuture(new RestException(Status.METHOD_NOT_ALLOWED,
                    "Termination of a system topic is not allowed"));
        }

        return validateTopicOperationAsync(topicName, TopicOperation.TERMINATE)
        .thenCompose(__ -> validateGlobalNamespaceOwnershipAsync(namespaceName))
        .thenCompose(__ -> validateTopicOwnershipAsync(topicName, authoritative))
        .thenCompose(__ -> getPartitionedTopicMetadataAsync(topicName, authoritative, false))
        .thenAccept(partitionMetadata -> {
            if (partitionMetadata.partitions > 0) {
                throw new RestException(Status.METHOD_NOT_ALLOWED,
                        "Termination of a partitioned topic is not allowed");
            }
        })
        .thenCompose(__ -> getTopicReferenceAsync(topicName))
        .thenCompose(topic -> {
            if (!(topic instanceof PersistentTopic)) {
                throw new RestException(Status.METHOD_NOT_ALLOWED,
                        "Termination of a non-persistent topic is not allowed");
            }
            return ((PersistentTopic) topic).terminate();
        });
    }

    protected void internalTerminatePartitionedTopic(AsyncResponse asyncResponse, boolean authoritative) {
        if (topicName.isPartitioned()) {
            String msg = "Termination of a non-partitioned topic is not allowed using partitioned-terminate"
                    + ", please use terminate commands";
            log.error().attr("topic", topicName).attr("msg", msg).log("");

View on GitHub (pinned to 820761864e)