apache/iceberg · error · NamespaceNotEmptyException

Namespace %s is not empty. One or more tables exist.

Error message

Namespace %s is not empty. One or more tables exist.

What it means

dropNamespace converts Hive Metastore's InvalidOperationException into Iceberg's NamespaceNotEmptyException because the database still contains tables. Hive refuses to drop a non-empty database, and the catalog surfaces that as a typed exception.

Source

Thrown at hive-metastore/src/main/java/org/apache/iceberg/hive/HiveCatalog.java:613

      return false;
    }

    try {
      clients.run(
          client -> {
            client.dropDatabase(
                namespace.level(0),
                false /* deleteData */,
                false /* ignoreUnknownDb */,
                false /* cascade */);
            return null;
          });

      LOG.info("Dropped namespace: {}", namespace);
      return true;

    } catch (InvalidOperationException e) {
      throw new NamespaceNotEmptyException(
          e, "Namespace %s is not empty. One or more tables exist.", namespace);

    } catch (NoSuchObjectException e) {
      return false;

    } catch (TException e) {
      throw new RuntimeException("Failed to drop namespace " + namespace + " in Hive Metastore", e);

    } catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      throw new RuntimeException(
          "Interrupted in call to drop dropDatabase(name) " + namespace + " in Hive Metastore", e);
    }
  }

  @Override
  public boolean setProperties(Namespace namespace, Map<String, String> properties) {
    Preconditions.checkArgument(

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Drop all tables first: iterate catalog.listTables(namespace) and drop each before dropping the namespace
  2. Use dropNamespace with purge/cascade semantics where supported, or run 'DROP DATABASE ... CASCADE' out-of-band if you accept deleting non-Iceberg objects
  3. Verify emptiness with listTables before dropping

Example fix

// before
catalog.dropNamespace(Namespace.of("legacy"), false);
// after
Namespace ns = Namespace.of("legacy");
for (TableIdentifier t : catalog.listTables(ns)) {
  catalog.dropTable(t);
}
catalog.dropNamespace(ns, false);
Defensive patterns

Strategy: validation

Validate before calling

Namespace ns = Namespace.of("legacy");
if (!catalog.listTables(ns).isEmpty()) {
  throw new IllegalStateException("Namespace " + ns + " still has tables");
}

Try / catch

try {
  catalog.dropNamespace(ns, false);
} catch (NamespaceNotEmptyException e) {
  catalog.listTables(ns).forEach(t -> catalog.dropTable(t));
  catalog.dropNamespace(ns, false);
}

Prevention

When it happens

Trigger: catalog.dropNamespace(Namespace.of("db"), false) when one or more tables/views still exist in the Hive database 'db'.

Common situations: Dropping a database before cleaning up tables; hidden tables registered via other engines (e.g. non-Iceberg tables) keeping the database non-empty; stale metadata listing omitted tables.

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


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/300479e192adfa3c. Report an issue: GitHub.