{"record":{"id":"6b6853f598509d84","repo":"apache/pulsar","slug":"cannot-delete-the-global-namespace-namespacename","errorCode":null,"errorMessage":"Cannot delete the global namespace ${namespaceName}. There are still more than one replication clusters configured.","messagePattern":"Cannot delete the global namespace (.+?)\\. There are still more than one replication clusters configured\\.","errorType":"http","errorClass":"RestException","httpStatus":412,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java","lineNumber":575,"sourceCode":"\n    @SuppressWarnings(\"deprecation\")\n    protected CompletableFuture<Void> internalDeleteNamespaceBundleAsync(String bundleRange, boolean authoritative,\n                                                                         boolean force) {\n        log.info()\n                .attr(\"namespace\", namespaceName)\n                .attr(\"bundleRange\", bundleRange)\n                .attr(\"authoritative\", authoritative)\n                .attr(\"force\", force)\n                .log(\"Deleting namespace bundle\");\n        return validateNamespaceOperationAsync(namespaceName, NamespaceOperation.DELETE_BUNDLE)\n                .thenCompose(__ -> validatePoliciesReadOnlyAccessAsync())\n                .thenCompose(__ -> getNamespacePoliciesAsync(namespaceName))\n                .thenCompose(policies -> {\n                    CompletableFuture<Void> future = CompletableFuture.completedFuture(null);\n                    String cluster = policies.getClusterThatCanDeleteNamespace();\n                    if (cluster == null) {\n                        // There are still more than one clusters configured for the global namespace\n                        throw new RestException(Status.PRECONDITION_FAILED, \"Cannot delete the global namespace \"\n                                + namespaceName\n                                + \". There are still more than one replication clusters configured.\");\n                    }\n                    if (!cluster.equals(config().getClusterName())) {\n                        // the only replication cluster is other cluster, redirect\n                        future = clusterResources().getClusterAsync(cluster)\n                                .thenCompose(clusterData -> {\n                                    if (clusterData.isEmpty()) {\n                                        throw new RestException(Status.NOT_FOUND,\n                                                \"Cluster \" + cluster + \" does not exist\");\n                                    }\n                                    ClusterData replClusterData = clusterData.get();\n                                    URL replClusterUrl;\n                                    try {\n                                        if (!config().isTlsEnabled() || !isRequestHttps()) {\n                                            replClusterUrl = new URL(replClusterData.getServiceUrl());\n                                        } else if (StringUtils.isNotBlank(replClusterData.getServiceUrlTls())) {\n                                            replClusterUrl = new URL(replClusterData.getServiceUrlTls());","sourceCodeStart":557,"sourceCodeEnd":593,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java#L557-L593","documentation":"A 412 PRECONDITION_FAILED thrown by internalDeleteNamespaceBundleAsync when deleting an individual namespace bundle of a global namespace whose policies still have more than one replication cluster, so getClusterThatCanDeleteNamespace() returns null. Bundle deletion, like namespace deletion, requires the namespace to be reduced to a single owning cluster first.","triggerScenarios":"DELETE /admin/v2/namespaces/{tenant}/{namespace}/{bundle} where the namespace policy's replication_clusters contains 2+ clusters. Also surfaces when operators try to clear bundles one-by-one as a workaround for the namespace-not-empty guard.","commonSituations":"Geo-replication cleanup scripts that unload/delete bundles before reducing replication clusters; partially drained namespaces where one cluster was removed but others remain; confusion between bundle-level and namespace-level delete requirements.","solutions":["Narrow replication clusters to one first (POST .../replication with a single cluster), then delete the bundle.","Delete the whole namespace with the documented cluster-narrowing flow instead of per-bundle deletes.","Inspect policies with GET .../replication to confirm the cluster count before bundle operations.","Ensure the remaining single cluster equals config().getClusterName() to avoid redirect paths."],"exampleFix":"// before\ncurl -X DELETE http://broker:8080/admin/v2/namespaces/t/ns/0x00000000_0xffffffff\n// 412\n\n// after\ncurl -X POST http://broker:8080/admin/v2/namespaces/t/ns/replication -d '[\"local\"]'\ncurl -X DELETE http://broker:8080/admin/v2/namespaces/t/ns/0x00000000_0xffffffff","handlingStrategy":"validation","validationCode":"const clusters = await admin.namespaces().getNamespaceReplicationClusters(tenant, ns);\nif (clusters.length !== 1) {\n  throw new Error(`bundle delete requires single replication cluster; current: ${clusters}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  await admin.namespaces().deleteNamespaceBundle(tenant, ns, bundle);\n} catch (e) {\n  if (e.getStatusCode() === 412) {\n    await admin.namespaces().setNamespaceReplicationClusters(tenant, ns, ['local']);\n  } else throw e;\n}","preventionTips":["Reduce replication clusters before any bundle-level delete","Don't use per-bundle deletes as a workaround for namespace cleanup","Verify cluster count via GET .../replication first"],"tags":["pulsar","geo-replication","bundle","precondition"],"backgroundTag":"replication-clusters-remaining","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}