apache/iceberg · error · AlreadyExistsException

Table already exists: %s.%s

Error message

Table already exists: %s.%s

What it means

HiveTableOperations.doCommit throws AlreadyExistsException 'Table already exists: %s.%s' when a create (newTable) commit finds an existing HMS entity that already has the Iceberg METADATA_LOCATION_PROP and is not a VIRTUAL_VIEW — a concurrent commit created the same table first. The commit fails rather than overwriting the other writer's table.

Source

Thrown at hive-metastore/src/main/java/org/apache/iceberg/hive/HiveTableOperations.java:286

    boolean updateHiveTable = false;

    HiveLock lock = lockObject(base != null ? base : tableMetadata);
    try {
      lock.lock();

      Table tbl = loadHmsTable();

      if (tbl != null) {
        // If we try to create the table but the metadata location is already set, then we had a
        // concurrent commit
        if (newTable
            && tbl.getParameters().get(BaseMetastoreTableOperations.METADATA_LOCATION_PROP)
                != null) {
          if (TableType.VIRTUAL_VIEW.name().equalsIgnoreCase(tbl.getTableType())) {
            throw new AlreadyExistsException(
                "View with same name already exists: %s.%s", database, tableName);
          }
          throw new AlreadyExistsException("Table already exists: %s.%s", database, tableName);
        }

        updateHiveTable = true;
        LOG.debug("Committing existing table: {}", fullName);
      } else {
        tbl =
            newHmsTable(
                tableMetadata.property(HiveCatalog.HMS_TABLE_OWNER, HiveHadoopUtil.currentUser()));
        LOG.debug("Committing new table: {}", fullName);
      }

      tbl.setSd(
          HiveOperationsBase.storageDescriptor(
              tableMetadata.schema(),
              tableMetadata.location(),
              hiveEngineEnabled)); // set to pickup any schema changes

      String metadataLocation = tbl.getParameters().get(METADATA_LOCATION_PROP);

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Catch AlreadyExistsException and load the existing table instead of creating (create-if-absent semantics).
  2. Guard creation with an external lock or a check-then-create inside one orchestrator to prevent races.
  3. If the existing table is unwanted, drop it first and then create, ensuring no other writer is committing.

Example fix

// before
Table t = catalog.createTable(ident, schema); // races with another writer
// after
Table t = catalog.tableExists(ident) ? catalog.loadTable(ident) : catalog.createTable(ident, schema);
Defensive patterns

Strategy: try-catch

Validate before calling

boolean exists = catalog.tableExists(ident); // create only when false, ideally behind a lock

Try / catch

try { table = catalog.createTable(ident, schema); } catch (AlreadyExistsException e) { table = catalog.loadTable(ident); }

Prevention

When it happens

Trigger: Two concurrent catalog.createTable calls for the same database.name: the loser reaches doCommit, HMS returns the existing table, and its metadata_location property is set (excluding a plain HMS leftover).

Common situations: Duplicate jobs creating the same table in parallel; orchestration retries racing; multi-engine setup where two frameworks initialize the same Iceberg table.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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