{"record":{"id":"c8ecf543647b68b7","repo":"apache/pulsar","slug":"broker-doesn-t-allow-forced-deletion-of-namespaces","errorCode":null,"errorMessage":"Broker doesn't allow forced deletion of namespaces","messagePattern":"Broker doesn't allow forced deletion of namespaces","errorType":"http","errorClass":"RestException","httpStatus":405,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java","lineNumber":480,"sourceCode":"            log.error()\n                    .exception(ex)\n                    .log(\"Get admin client error when preparing to delete topics.\");\n            return FutureUtil.failedFuture(ex);\n        }\n        List<CompletableFuture<Void>> futures = new ArrayList<>();\n        for (String topicName : topicNames) {\n            futures.add(admin.topics().deleteAsync(topicName, true));\n        }\n        return FutureUtil.waitForAll(futures);\n    }\n\n    private CompletableFuture<Policies> precheckWhenDeleteNamespace(NamespaceName nsName, boolean force) {\n        CompletableFuture<Policies> preconditionCheck =\n                validateTenantOperationAsync(nsName.getTenant(), TenantOperation.DELETE_NAMESPACE)\n                        .thenCompose(__ -> validatePoliciesReadOnlyAccessAsync())\n                        .thenCompose(__ -> {\n                            if (force && !pulsar().getConfiguration().isForceDeleteNamespaceAllowed()) {\n                                throw new RestException(Status.METHOD_NOT_ALLOWED,\n                                        \"Broker doesn't allow forced deletion of namespaces\");\n                            }\n                            return CompletableFuture.completedFuture(null);\n                        })\n                        .thenCompose(__ -> namespaceResources().getPoliciesAsync(nsName))\n                        .thenCompose(policiesOpt -> {\n                            if (policiesOpt.isEmpty()) {\n                                throw new RestException(Status.NOT_FOUND, \"Namespace \" + nsName + \" does not exist.\");\n                            }\n                            Policies policies = policiesOpt.get();\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,\n                                    \"Cannot delete the global namespace \" + nsName + \". There are still more than \"\n                                    + \"one replication clusters configured or replication clusters is empty.\");\n                            }\n                            if (!cluster.equals(config().getClusterName())) {","sourceCodeStart":462,"sourceCodeEnd":498,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java#L462-L498","documentation":"A 405 METHOD_NOT_ALLOWED thrown during precheckWhenDeleteNamespace when a namespace delete is requested with force=true but the broker's isForceDeleteNamespaceAllowed configuration is false (the default). The broker intentionally refuses force deletion of namespaces as a data-safety guard; callers must either disable forcing or enable the broker-level allowance.","triggerScenarios":"Calling DELETE /admin/v2/namespaces/{tenant}/{namespace}?force=true while pulsar.broker.forceDeleteNamespaceAllowed=false on the target broker. The check runs after tenant DELETE_NAMESPACE authorization and policies read-only validation, before any topics are inspected.","commonSituations":"Operators scripting namespace teardown with force=true on a default-configured broker; cluster-to-cluster config drift where dev brokers allow force delete but prod does not; upgrading Pulsar where the guard default was changed/adopted.","solutions":["Set forceDeleteNamespaceAllowed=true in broker.conf / the broker.conf of the target cluster if force deletion is intended policy.","Remove force=true from the delete request and delete topics explicitly first (safer).","Apply the config via broker admin/configmanager endpoint or Helm values if running on Kubernetes, then retry.","Verify which cluster you are hitting — a redirect to another cluster may surface a broker with a different force-delete policy."],"exampleFix":"# before (broker.conf)\nforceDeleteNamespaceAllowed=false\n\n# after\nforceDeleteNamespaceAllowed=true\n# then retry:\ncurl -X DELETE 'http://broker:8080/admin/v2/namespaces/my-tenant/my-ns?force=true'","handlingStrategy":"validation","validationCode":"// check broker policy before sending force delete\nconst ok = await fetch(`${baseUrl}/admin/v2/broker-runtime`);\n// or simply omit force unless you've confirmed forceDeleteNamespaceAllowed=true in broker.conf","typeGuard":null,"tryCatchPattern":"try {\n  await admin.namespaces().deleteNamespace(tenant, ns, true);\n} catch (e) {\n  if (e.getStatusCode() === 405) {\n    // fall back to non-forced deletion after draining topics\n  } else throw e;\n}","preventionTips":["Confirm forceDeleteNamespaceAllowed=true before using force=true","Prefer explicit topic deletion over force delete","Align this config across dev/prod clusters to avoid script drift"],"tags":["pulsar","configuration","namespace-deletion","force-delete"],"backgroundTag":"operation-not-allowed-by-config","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"}