apache/pulsar · error · RestException
Cannot delete non empty bundle
Error message
Cannot delete non empty bundle
What it means
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.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java:631
});
}
return future
.thenCompose(__ ->
validateNamespaceBundleOwnershipAsync(namespaceName, bundleRange,
authoritative, true))
.thenCompose(bundle -> {
return pulsar().getNamespaceService().getListOfPersistentTopics(namespaceName)
.thenCompose(topics -> {
CompletableFuture<Void> deleteTopicsFuture =
CompletableFuture.completedFuture(null);
if (!force) {
List<CompletableFuture<NamespaceBundle>> futures = new ArrayList<>();
for (String topic : topics) {
futures.add(pulsar().getNamespaceService()
.getBundleAsync(TopicName.get(topic))
.thenCompose(topicBundle -> {
if (bundle.equals(topicBundle)) {
throw new RestException(Status.CONFLICT,
"Cannot delete non empty bundle");
}
return CompletableFuture.completedFuture(null);
}));
}
deleteTopicsFuture = FutureUtil.waitForAll(futures);
}
return deleteTopicsFuture.thenCompose(
___ -> pulsar().getNamespaceService()
.removeOwnedServiceUnitAsync(bundle))
.thenRun(() -> pulsar().getBrokerService().getBundleStats()
.remove(bundle.toString()));
});
});
});
}
View on GitHub (pinned to 820761864e)
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.
Example fix
// before curl -X DELETE http://broker:8080/admin/v2/namespaces/t/ns/0x00000000_0x40000000 // 409 Cannot delete non empty bundle // after curl -X PUT http://broker:8080/admin/v2/namespaces/t/ns/0x00000000_0x40000000/unload curl -X DELETE http://broker:8080/admin/v2/namespaces/t/ns/0x00000000_0x40000000
Defensive patterns
Strategy: try-catch
Validate before calling
const topics = await admin.namespaces().getTopics(tenant, ns); // persistent+non-persistent
for (const t of topics) {
const bundleOfTopic = await admin.lookups().getBundleRange ? null : null;
}
// simplest pre-check: ensure topic list is empty before deleting the bundle
if (topics.length > 0) throw new Error('topics still present; unload/delete before bundle delete'); Try / catch
try {
await admin.namespaces().deleteNamespaceBundle(tenant, ns, bundle);
} catch (e) {
if (e.getStatusCode() === 409) {
await admin.namespaces().unloadNamespaceBundle(tenant, ns, bundle);
await admin.namespaces().deleteNamespaceBundle(tenant, ns, bundle);
} else throw e;
} Prevention
- 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)
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Cannot delete non empty namespace
- Cannot delete the global namespace ${namespaceName}. There a
- %s is a non-partitioned topic. Instead of calling delete-par
- Resource group already exists:${rgName}
- Compaction already in progress
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/984a305f9849eec6.
Report an issue: GitHub.