{"record":{"id":"6ee4a426371be59a","repo":"apache/pulsar","slug":"cluster-not-empty","errorCode":null,"errorMessage":"Cluster not empty","messagePattern":"Cluster not empty","errorType":"http","errorClass":"RestException","httpStatus":412,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/ClustersBase.java","lineNumber":522,"sourceCode":"                                .log(\"Failed to delete cluster - Does not exist\");\n                        asyncResponse.resume(new RestException(Status.NOT_FOUND, \"Cluster does not exist\"));\n                        return null;\n                    }\n                    log.error()\n                            .attr(\"cluster\", cluster)\n                            .exception(ex)\n                            .log(\"Failed to delete cluster\");\n                    resumeAsyncResponseExceptionally(asyncResponse, ex);\n                    return null;\n                });\n    }\n\n    private CompletableFuture<Void> internalDeleteClusterAsync(String cluster) {\n        // Check that the cluster is not used by any tenant (eg: no namespaces provisioned there)\n        return pulsar().getPulsarResources().getClusterResources().isClusterUsedAsync(cluster)\n                .thenCompose(isClusterUsed -> {\n                    if (isClusterUsed) {\n                        throw new RestException(PRECONDITION_FAILED, \"Cluster not empty\");\n                    }\n                    // check the namespaceIsolationPolicies associated with the cluster\n                    return namespaceIsolationPolicies().getIsolationDataPoliciesAsync(cluster);\n                }).thenCompose(nsIsolationPoliciesOpt -> {\n                    if (nsIsolationPoliciesOpt.isPresent()) {\n                        if (!nsIsolationPoliciesOpt.get().getPolicies().isEmpty()) {\n                            throw new RestException(PRECONDITION_FAILED, \"Cluster not empty\");\n                        }\n                        // Need to delete the isolation policies if present\n                        return namespaceIsolationPolicies().deleteIsolationDataAsync(cluster);\n                    }\n                    return CompletableFuture.completedFuture(null);\n                }).thenCompose(unused -> clusterResources()\n                        .getFailureDomainResources().deleteFailureDomainsAsync(cluster)\n                        .thenCompose(__ -> clusterResources().deleteClusterAsync(cluster)));\n    }\n\n    @GET","sourceCodeStart":504,"sourceCodeEnd":540,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/ClustersBase.java#L504-L540","documentation":"HTTP 412 (PRECONDITION_FAILED) thrown when deleting a cluster that is still referenced by at least one tenant/namespace. internalDeleteClusterAsync first calls isClusterUsedAsync; if any namespace is provisioned on the cluster, deletion is refused to avoid orphaning namespaces. This is the tenant-usage branch of the emptiness check.","triggerScenarios":"DELETE /admin/v3/clusters/{cluster} while namespaces or tenants still reference the cluster; decommissioning a cluster before migrating its namespaces; deleting a cluster used in a tenant's allowedCluster list with provisioned namespaces.","commonSituations":"Cluster teardown runbooks executed out of order; old clusters with forgotten namespaces; replication cluster removal while its local namespaces still exist.","solutions":["Migrate or delete all namespaces on the cluster: for each namespace, either delete it or remove the cluster from its replication clusters / allowed clusters.","Update tenants (PUT /admin/v3/tenants/{tenant}) to drop this cluster from allowedClusters where no namespaces remain.","Run GET /admin/v3/namespaces (filtered by cluster) to enumerate what still references the cluster, then clean up.","Retry DELETE once the cluster is unused."],"exampleFix":"// before\nadmin.clusters().deleteCluster(\"old-cluster\"); // 412 Cluster not empty\n// after\nfor (String ns : admin.namespaces().getNamespaces(\"my-tenant\")) {\n    if (admin.namespaces().getReplicationClusters(ns).contains(\"old-cluster\")) {\n        admin.namespaces().deleteNamespace(ns); // or update replication clusters\n    }\n}\nadmin.clusters().deleteCluster(\"old-cluster\");","handlingStrategy":"validation","validationCode":"// ensure no namespaces/tenants reference the cluster before deleting\nfor (String ns : admin.namespaces().getNamespaces()) {\n    if (admin.namespaces().getReplicationClusters(ns).contains(cluster)) {\n        throw new IllegalStateException(\"Namespace \" + ns + \" still uses cluster \" + cluster);\n    }\n}\nadmin.clusters().deleteCluster(cluster);","typeGuard":null,"tryCatchPattern":"try {\n    admin.clusters().deleteCluster(cluster);\n} catch (PulsarAdminException.PreconditionFailedException e) {\n    // \"Cluster not empty\": migrate/delete namespaces referencing it, then retry\n}","preventionTips":["Order teardown: namespaces -> tenants' allowedClusters -> isolation policies -> cluster.","Audit replication-cluster references across all namespaces before decommissioning.","Automate the enumeration of referencing namespaces in deletion runbooks."],"tags":["rest-api","precondition-failed","cluster-deletion","namespace"],"backgroundTag":"resource-in-use","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}