apache/iceberg · error · org.apache.spark.sql.catalyst.analysis.TableAlreadyExistsException

TableAlreadyExistsException

Error message

TableAlreadyExistsException: <to>

What it means

When the rename target identifier already exists, the Iceberg catalog throws AlreadyExistsException and SparkCatalog translates it to Spark's TableAlreadyExistsException for the target name. Renames in Iceberg catalogs are fail-fast and never overwrite an existing table.

Solutions

  1. Drop or rename the existing target table first
  2. Choose a unique target name (e.g. append a timestamp)
  3. Check for the target's existence before renaming
  4. Handle TableAlreadyExistsException and skip the rename on retries

Example fix

// before
spark.sql("ALTER TABLE db.t RENAME TO db.archive") // db.archive exists
// after
spark.sql("ALTER TABLE db.t RENAME TO db.archive_" + System.currentTimeMillis())
Defensive patterns

Strategy: validation

Validate before calling

if (spark.catalog().tableExists(toIdent)) { throw new IllegalStateException("target already exists"); }

Try / catch

try { spark.sql("ALTER TABLE db.t RENAME TO db.t2"); } catch (TableAlreadyExistsException e) { /* pick another target or skip */ }

Prevention

When it happens

Trigger: ALTER TABLE ... RENAME TO where the destination table already exists in the same namespace; retrying a rename that partially appeared to succeed.

Common situations: Job retries re-attempting a completed rename; staging table naming collisions; automated pipelines generating deterministic target names.

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/118b08b9085bd1f8. Report an issue: GitHub.

Appendix: source

Thrown at spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/SparkCatalog.java:383

  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)