apache/pulsar · error · RestException
Scalable topic not found: ${tn}
Error message
Scalable topic not found: ${tn} What it means
HTTP 404 thrown by the scalable-topic delete endpoint when no scalable-topic metadata exists for the named topic — meaning the topic is not a scalable topic (or was already deleted). Deletion only proceeds on topics registered as scalable in the metadata store.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/v2/ScalableTopics.java:668
@ApiResponse(responseCode = "500", description = "Internal server error")})
public void deleteScalableTopic(
@Suspended final AsyncResponse asyncResponse,
@Parameter(description = "Specify the tenant", required = true)
@PathParam("tenant") String tenant,
@Parameter(description = "Specify the namespace", required = true)
@PathParam("namespace") String namespace,
@Parameter(description = "Specify topic name", required = true)
@PathParam("topic") @Encoded String encodedTopic,
@Parameter(description = "Force deletion even if topic has active subscriptions")
@QueryParam("force") @DefaultValue("false") boolean force) {
validateNamespaceName(tenant, namespace);
TopicName tn = TopicName.get(TopicDomain.topic.value(), namespaceName, encodedTopic);
validateNamespaceOperationAsync(namespaceName, NamespaceOperation.DELETE_TOPIC)
.thenCompose(__ -> resources().getScalableTopicMetadataAsync(tn))
.thenCompose(optMd -> {
if (optMd.isEmpty()) {
throw new RestException(Response.Status.NOT_FOUND,
"Scalable topic not found: " + tn);
}
// Delete metadata first, then best-effort clean up segment topics
return resources().deleteScalableTopicAsync(tn)
.thenCompose(__ -> deleteSegmentTopics(tn, optMd.get(), force));
})
.thenAccept(__ -> {
log.info().attr("clientAppId", clientAppId()).attr("topic", tn)
.log("Deleted scalable topic");
asyncResponse.resume(Response.noContent().build());
})
.exceptionally(ex -> {
log.error().attr("clientAppId", clientAppId()).attr("topic", tn)
.exception(ex).log("Failed to delete scalable topic");
resumeAsyncResponseExceptionally(asyncResponse, ex);
return null;
});
}View on GitHub (pinned to 820761864e)
Solutions
- Verify the topic is a scalable topic (GET its scalable metadata) before deleting, and skip/short-circuit if absent.
- Use the regular topics delete API for non-scalable topics.
- Treat 404 as success in idempotent cleanup automation.
Example fix
// before
await admin.scalableTopics().deleteScalableTopic(tenant, ns, topic); // throws 404 on retry
// after
if (await admin.scalableTopics().getScalableTopicMetadataAsync(tenant, ns, topic).isPresent()) {
await admin.scalableTopics().deleteScalableTopic(tenant, ns, topic);
} Defensive patterns
Strategy: validation
Validate before calling
const md = await admin.scalableTopics().getScalableTopicMetadataAsync(tenant, ns, topic); if (md.isEmpty()) return; // not a scalable topic / already deleted — nothing to delete
Type guard
const isScalableTopic = (metadataOpt) => metadataOpt != null && metadataOpt.isPresent();
Try / catch
try {
await admin.scalableTopics().deleteScalableTopic(tenant, ns, topic, /*force*/ true);
} catch (e) {
if (e.status === 404) return; // idempotent delete
throw e;
} Prevention
- Route deletions by topic kind: scalable-topics API for scalable topics, topics API for regular ones.
- Implement cleanup as idempotent (treat 404 as success).
- Confirm the topic name/tenant/namespace before bulk deletes.
When it happens
Trigger: Calling DELETE /admin/v2/scalable-topics/{tenant}/{namespace}/{topic} for a topic that was never created as scalable, an already-deleted scalable topic (double delete), or a regular/partitioned topic passed by mistake.
Common situations: Cleanup scripts deleting a whole namespace's topics using the wrong admin resource (scalable-topics vs topics); idempotent deletion retries after a first successful delete; typos in the topic name.
Related errors
- Topic does not exist: ${persistentBase}
- Segment topic not found: ${segmentTopic}
- Segment topic not loaded: ${segmentTopic}
- Subscription not found on segment: ${subscription}
- numInitialSegments must be >= 1
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/e5e56eea704af604.
Report an issue: GitHub.