apache/iceberg · error · org.apache.iceberg.exceptions.AlreadyExistsException

Cannot create table as it already exists

Error message

Cannot create table %s as it already exists

What it means

During staging of the destination table, destCatalog().stageCreate throws Spark's TableAlreadyExistsException when a table with the destination identifier already exists. The action converts it into Iceberg's AlreadyExistsException with this message.

Solutions

  1. Choose a unique destination table name
  2. Drop or rename the existing destination table before re-running the action
  3. If re-running after failure, remove the leftover staged table from the destination catalog

Example fix

// before
CALL iceberg.system.snapshot('source_tbl', 'db.existing_tbl')
// after
DROP TABLE db.existing_tbl; -- or pick a new name
CALL iceberg.system.snapshot('source_tbl', 'db.new_tbl');
Defensive patterns

Strategy: validation

Validate before calling

// Check destination does not exist before running
spark.sql("DESCRIBE TABLE db.dest_tbl"); // throws if absent -> safe to proceed

Try / catch

try { ... } catch (AlreadyExistsException e) { /* pick a new name or drop existing table */ }

Prevention

When it happens

Trigger: Running MIGRATE or SNAPSHOT where the destination table name already exists in the destination catalog; re-running a previously failed/partial migrate after the destination was already created.

Common situations: Re-running a failed migration without cleaning up the staged destination table; accidentally reusing an existing table name; OR REPLACE not supported for this procedure path.

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/751a47128a279f00. Report an issue: GitHub.

Appendix: source

Thrown at spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/actions/BaseTableCreationSparkAction.java:167

        catalog.getClass().getName(),
        SparkSessionCatalog.class.getName(),
        SparkCatalog.class.getName());

    return (StagingTableCatalog) catalog;
  }

  protected StagedSparkTable stageDestTable() {
    try {
      Map<String, String> props = destTableProps();
      StructType schema = sourceTable.schema();
      Transform[] partitioning = sourceTable.partitioning();
      return (StagedSparkTable)
          destCatalog().stageCreate(destTableIdent(), schema, partitioning, props);
    } catch (org.apache.spark.sql.catalyst.analysis.NoSuchNamespaceException e) {
      throw new NoSuchNamespaceException(
          "Cannot create table %s as the namespace does not exist", destTableIdent());
    } catch (org.apache.spark.sql.catalyst.analysis.TableAlreadyExistsException e) {
      throw new AlreadyExistsException(
          "Cannot create table %s as it already exists", destTableIdent());
    }
  }

  protected void ensureNameMappingPresent(Table table) {
    if (!table.properties().containsKey(TableProperties.DEFAULT_NAME_MAPPING)) {
      NameMapping nameMapping = MappingUtil.create(table.schema());
      String nameMappingJson = NameMappingParser.toJson(nameMapping);
      table.updateProperties().set(TableProperties.DEFAULT_NAME_MAPPING, nameMappingJson).commit();
    }
  }

  protected String getMetadataLocation(Table table) {
    String defaultValue =
        LocationUtil.stripTrailingSlash(table.location()) + "/" + ICEBERG_METADATA_FOLDER;
    return LocationUtil.stripTrailingSlash(
        table.properties().getOrDefault(TableProperties.WRITE_METADATA_LOCATION, defaultValue));
  }

View on GitHub (pinned to 86d9c8fc54)