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
- List subscriptions with `pulsar-admin topics subscriptions <topic>` and fix the name.
- Handle 404 as success in idempotent deletion logic.
- If per-partition state diverged on a partitioned topic, run the delete against the partitioned parent so all partitions converge.
- 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
- Confirm subscription names via topics subscriptions command
- Avoid double-invoked cleanup without existence checks
- Operate on the partitioned parent rather than individual partitions to keep state consistent
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
- Subscription not found
- Subscription has active connected consumers
- cluster data is required
- Cannot delete non empty namespace
- Namespace does not exist
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/7bf854c9762b93e6.
Report an issue: GitHub.