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

  1. Verify the topic is a scalable topic (GET its scalable metadata) before deleting, and skip/short-circuit if absent.
  2. Use the regular topics delete API for non-scalable topics.
  3. 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

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


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