apache/iceberg · error · NamespaceNotEmptyException
Namespace '%s' is not empty.
Error message
Namespace '%s' is not empty.
What it means
When the drop commit conflicts server-side with NAMESPACE_NOT_EMPTY, the client rethrows as NamespaceNotEmptyException. Nessie enforces that a namespace key cannot be deleted while nested namespaces or tables still live under it.
Source
Thrown at nessie/src/main/java/org/apache/iceberg/nessie/NessieIcebergClient.java:349
"Content object with name '%s' is not a namespace.", namespace);
}
try {
commitRetry("drop namespace " + key, Operation.Delete.of(key));
return true;
} catch (NessieReferenceConflictException e) {
Optional<Conflict> conflict =
NessieUtil.extractSingleConflict(
e,
EnumSet.of(
Conflict.ConflictType.KEY_DOES_NOT_EXIST,
Conflict.ConflictType.NAMESPACE_NOT_EMPTY));
if (conflict.isPresent()) {
Conflict.ConflictType conflictType = conflict.get().conflictType();
switch (conflictType) {
case KEY_DOES_NOT_EXIST:
return false;
case NAMESPACE_NOT_EMPTY:
throw new NamespaceNotEmptyException(e, "Namespace '%s' is not empty.", namespace);
}
}
throw new UncheckedIOException(
String.format("Cannot drop namespace '%s': %s", namespace, e.getMessage()), e);
}
} catch (NessieNotFoundException e) {
LOG.error(
"Cannot drop namespace '{}': ref '{}' is no longer valid.",
namespace,
getRef().getName(),
e);
} catch (BaseNessieClientServerException e) {
throw new UncheckedIOException(
String.format("Cannot drop namespace '%s': %s", namespace, e.getMessage()), e);
}
return false;
}
View on GitHub (pinned to 86d9c8fc54)
Solutions
- Drop or move all tables and views under the namespace first (listTables + dropTable).
- Drop child namespaces (deepest first) before the parent.
- Re-check after concurrent writers: re-list contents and retry the drop.
Example fix
// before
catalog.dropNamespace(Namespace.of("a")); // still contains tables
// after
for (TableIdentifier t : catalog.listTables(Namespace.of("a"))) {
catalog.dropTable(t);
}
catalog.dropNamespace(Namespace.of("a")); Defensive patterns
Strategy: validation
Validate before calling
boolean empty = catalog.listTables(ns).isEmpty()
&& catalog.listNamespaces(ns).isEmpty();
if (!empty) throw new IllegalStateException("Namespace not empty: " + ns); Try / catch
try {
return catalog.dropNamespace(ns);
} catch (NamespaceNotEmptyException e) {
// drain children deepest-first, then retry
return drainAndDrop(ns);
} Prevention
- Always list and drop children (tables, views, sub-namespaces) before the parent.
- Treat drop-namespace as a bottom-up cleanup operation.
- Re-check emptiness right before dropping in concurrent environments.
When it happens
Trigger: Calling Catalog.dropNamespace(Namespace.of("a")) while tables/views or child namespaces like 'a.b' or 'a.tbl' still exist under it on the current ref.
Common situations: Trying to clean up a parent namespace before migrating its tables; concurrent writers added a table between your check and the drop.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Cannot create namespace '%s': parent namespace '%s' does not
- Cannot create namespace '%s': %s
- Cannot drop namespace '%s': %s
- Cannot update properties on namespace '%s': %s
- Namespace already exists: %s
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/7251e8935d3f8401.
Report an issue: GitHub.