apache/iceberg · error · TableAlreadyExistsException

TableAlreadyExistsException(to)

Error message

TableAlreadyExistsException(to)

What it means

Spark's renameTable failed because the target identifier already exists in the underlying Iceberg catalog; the Iceberg AlreadyExistsException was caught and rethrown as Spark's TableAlreadyExistsException. Iceberg catalogs refuse to overwrite an existing table during rename.

Source

Thrown at spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/SparkCatalog.java:432

  private boolean catalogDropTable(Identifier ident) {
    if (isPathIdentifier(ident)) {
      return tables.dropTable(((PathIdentifier) ident).location(), false /* don't purge data */);
    } else {
      return icebergCatalog.dropTable(buildIdentifier(ident), false /* don't purge data */);
    }
  }

  @Override
  public void renameTable(Identifier from, Identifier to)
      throws NoSuchTableException, TableAlreadyExistsException {
    try {
      checkNotPathIdentifier(from, "renameTable");
      checkNotPathIdentifier(to, "renameTable");
      icebergCatalog.renameTable(buildIdentifier(from), buildIdentifier(to));
    } catch (org.apache.iceberg.exceptions.NoSuchTableException e) {
      throw new NoSuchTableException(from);
    } catch (AlreadyExistsException e) {
      throw new TableAlreadyExistsException(to);
    }
  }

  @Override
  public void invalidateTable(Identifier ident) {
    if (!isPathIdentifier(ident)) {
      icebergCatalog.invalidateTable(buildIdentifier(ident));
    }
  }

  @Override
  public Identifier[] listTables(String[] namespace) {
    return icebergCatalog.listTables(Namespace.of(namespace)).stream()
        .map(ident -> Identifier.of(ident.namespace().levels(), ident.name()))
        .toArray(Identifier[]::new);
  }

  @Override

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Check target existence with spark.catalog().tableExists and drop or pick a different target name first.
  2. If a retry is expected, drop the target table before re-running the rename.
  3. Use unique, time-suffixed target names in automated pipelines.
  4. If the stale target is unwanted, remove it via the same Iceberg catalog (not just the metastore).

Example fix

// before
spark.sql("ALTER TABLE mydb.orders RENAME TO mydb.orders_v2") // TableAlreadyExistsException

// after
if (spark.catalog().tableExists("iceberg_catalog.mydb.orders_v2")) {
  spark.sql("DROP TABLE iceberg_catalog.mydb.orders_v2")
}
spark.sql("ALTER TABLE iceberg_catalog.mydb.orders RENAME TO iceberg_catalog.mydb.orders_v2")
Defensive patterns

Strategy: validation

Validate before calling

if (spark.catalog().tableExists("iceberg_catalog.mydb.orders_v2")) {
  spark.sql("DROP TABLE iceberg_catalog.mydb.orders_v2");
}

Try / catch

try {
  catalog.renameTable(from, to);
} catch (TableAlreadyExistsException e) {
  // target exists: drop it or choose another name, then retry once
}

Prevention

When it happens

Trigger: ALTER TABLE from RENAME TO to where 'to' is an existing table; retried rename jobs where a previous attempt partially succeeded; creating a backup-name workflow that reuses an old target name without dropping it first.

Common situations: Idempotent pipelines that rerun renames; users expecting RENAME to overwrite like some engines do; schema-evolution scripts that cycle names (orders -> orders_old -> orders).

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/4576be2617a48475. Report an issue: GitHub.