apache/pulsar · error · RestException

getSubNotFoundErrorMessage(topicName.toString(), subName)

Error message

getSubNotFoundErrorMessage(topicName.toString(), subName)

What it means

HTTP 404 thrown when performing a subscription operation (delete, or the deleteSubscriptionAsync path shown) on a specific topic where topic.getSubscription(subName) returns null — the topic exists but that particular subscription does not. Unlike the partitioned aggregation path, this check is per topic reference, using the standard sub-not-found message with topic and subscription names.

Source

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

                            return null;
                        });
                    }
                    return internalDeleteSubscriptionForNonPartitionedTopicAsync(subName, authoritative, force);
                });
            }
        });
    }

    // Note: this method expects the caller to check authorization
    private CompletableFuture<Void> internalDeleteSubscriptionForNonPartitionedTopicAsync(String subName,
                                                                                          boolean authoritative,
                                                                                          boolean force) {
        return validateTopicOwnershipAsync(topicName, authoritative)
                .thenCompose(__ -> getTopicReferenceAsync(topicName))
                .thenCompose((topic) -> {
                    Subscription sub = topic.getSubscription(subName);
                    if (sub == null) {
                        throw new RestException(Status.NOT_FOUND,
                                getSubNotFoundErrorMessage(topicName.toString(), subName));
                    }
                    return force ? sub.deleteForcefully() : sub.delete();
                });
    }

    private void internalAnalyzeSubscriptionBacklogForNonPartitionedTopic(AsyncResponse asyncResponse,
                                                                          String subName,
                                                                          Optional<Position> position,
                                                                          boolean authoritative) {
        validateTopicOwnershipAsync(topicName, authoritative)
                .thenCompose(__ -> validateTopicOperationAsync(topicName, TopicOperation.CONSUME, subName))
                .thenCompose(__ -> getTopicReferenceAsync(topicName))
                .thenCompose(topic -> {
                            Subscription sub = topic.getSubscription(subName);
                            if (sub == null) {
                                throw new RestException(Status.NOT_FOUND,
                                        getSubNotFoundErrorMessage(topicName.toString(), subName));

View on GitHub (pinned to 820761864e)

Solutions

  1. List subscriptions with `pulsar-admin topics subscriptions <topic>` and fix the name.
  2. Handle 404 as success in idempotent deletion logic.
  3. If per-partition state diverged on a partitioned topic, run the delete against the partitioned parent so all partitions converge.
  4. Check subscription expiration / auto-delete config that may have removed the subscription.

Example fix

// before
admin.topics().deleteSubscription("persistent://t/ns/topic-partition-0", "subb"); // 404 typo
// after
admin.topics().subscriptions("persistent://t/ns/topic-partition-0").forEach(s -> {
    if ("mysub".equals(s)) admin.topics().deleteSubscription("persistent://t/ns/topic-partition-0", s);
});
Defensive patterns

Strategy: try-catch

Validate before calling

if (!admin.topics().getSubscriptions(topic).contains(subName)) {
    return; // subscription absent
}
admin.topics().deleteSubscription(topic, subName);

Try / catch

try {
    admin.topics().deleteSubscription(topic, subName);
} catch (PulsarAdminException.NotFoundException e) {
    // no such subscription; idempotent success
}

Prevention

When it happens

Trigger: DELETE /admin/v2/persistent/{ns}/{topic}/subscription/{subName} (force or normal) where the subscription cursor was never created or was already removed on that topic/partition.

Common situations: Typo in subscription name; unsubscribe racing with the subscription's auto-deletion after consumers left; operating on a partition that never had the subscription while others did; double-invocation of cleanup tooling.

Related errors


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