{"record":{"id":"984a305f9849eec6","repo":"apache/pulsar","slug":"cannot-delete-non-empty-bundle","errorCode":null,"errorMessage":"Cannot delete non empty bundle","messagePattern":"Cannot delete non empty bundle","errorType":"http","errorClass":"RestException","httpStatus":409,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java","lineNumber":631,"sourceCode":"                                });\n                    }\n                    return future\n                            .thenCompose(__ ->\n                                    validateNamespaceBundleOwnershipAsync(namespaceName, bundleRange,\n                                            authoritative, true))\n                            .thenCompose(bundle -> {\n                                return pulsar().getNamespaceService().getListOfPersistentTopics(namespaceName)\n                                        .thenCompose(topics -> {\n                                            CompletableFuture<Void> deleteTopicsFuture =\n                                                    CompletableFuture.completedFuture(null);\n                                            if (!force) {\n                                                List<CompletableFuture<NamespaceBundle>> futures = new ArrayList<>();\n                                                for (String topic : topics) {\n                                                    futures.add(pulsar().getNamespaceService()\n                                                            .getBundleAsync(TopicName.get(topic))\n                                                            .thenCompose(topicBundle -> {\n                                                                if (bundle.equals(topicBundle)) {\n                                                                    throw new RestException(Status.CONFLICT,\n                                                                            \"Cannot delete non empty bundle\");\n                                                                }\n                                                                return CompletableFuture.completedFuture(null);\n                                                            }));\n\n                                                }\n                                                deleteTopicsFuture = FutureUtil.waitForAll(futures);\n                                            }\n                                            return deleteTopicsFuture.thenCompose(\n                                                            ___ -> pulsar().getNamespaceService()\n                                                                    .removeOwnedServiceUnitAsync(bundle))\n                                                    .thenRun(() -> pulsar().getBrokerService().getBundleStats()\n                                                            .remove(bundle.toString()));\n                                        });\n                            });\n                });\n    }\n","sourceCodeStart":613,"sourceCodeEnd":649,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java#L613-L649","documentation":"A 409 CONFLICT thrown by internalDeleteNamespaceBundleAsync when the bundle being deleted still owns at least one topic: for each topic in the namespace, the broker resolves its bundle via NamespaceService.getBundleAsync and aborts if any topic maps to the target bundle. This prevents deleting a bundle that would strand live topics.","triggerScenarios":"DELETE /admin/v2/namespaces/{tenant}/{namespace}/{bundle} while topics are still assigned to that bundle. Also occurs when a topic was just created (auto-creation) or a lookup pinned the topic into the bundle between listing and deletion.","commonSituations":"Operators trying to force-unload a namespace by deleting bundles without unloading topics first; sticky lookups from active consumers recreating bundle ownership; race in scripts that delete topics then immediately delete bundles before ownership clears.","solutions":["Unload the bundle first: PUT /admin/v2/namespaces/{tenant}/{namespace}/{bundle}/unload, then delete it.","Delete or migrate all topics in the bundle, verify with GET .../namespaces/{tenant}/{namespace}/topics, then delete the bundle.","Use clear-backlog and disconnect-clients on the bundle before deletion to remove active ownership.","Retry after a short delay if topics were just deleted — bundle ownership update may lag."],"exampleFix":"// before\ncurl -X DELETE http://broker:8080/admin/v2/namespaces/t/ns/0x00000000_0x40000000\n// 409 Cannot delete non empty bundle\n\n// after\ncurl -X PUT http://broker:8080/admin/v2/namespaces/t/ns/0x00000000_0x40000000/unload\ncurl -X DELETE http://broker:8080/admin/v2/namespaces/t/ns/0x00000000_0x40000000","handlingStrategy":"try-catch","validationCode":"const topics = await admin.namespaces().getTopics(tenant, ns); // persistent+non-persistent\nfor (const t of topics) {\n  const bundleOfTopic = await admin.lookups().getBundleRange ? null : null;\n}\n// simplest pre-check: ensure topic list is empty before deleting the bundle\nif (topics.length > 0) throw new Error('topics still present; unload/delete before bundle delete');","typeGuard":null,"tryCatchPattern":"try {\n  await admin.namespaces().deleteNamespaceBundle(tenant, ns, bundle);\n} catch (e) {\n  if (e.getStatusCode() === 409) {\n    await admin.namespaces().unloadNamespaceBundle(tenant, ns, bundle);\n    await admin.namespaces().deleteNamespaceBundle(tenant, ns, bundle);\n  } else throw e;\n}","preventionTips":["Unload the bundle before deleting it","Disconnect clients and clear backlog to drop active lookups","Account for auto-created topics racing with deletion","Retry after delay when topics were just removed (ownership lag)"],"tags":["pulsar","bundle","conflict","topic-ownership"],"backgroundTag":"bundle-not-empty","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"}