apache/iceberg · error · org.apache.flink.table.catalog.exceptions.AlreadyExistsException

Table %s already exists in the database %s and catalog %s

Error message

Table %s already exists in the database %s and catalog %s

What it means

createTableLoader checks tableExists and then attempts createIcebergTable with ignoreIfExists semantics. If TableAlreadyExistException is thrown anyway (e.g., concurrent creation), it is wrapped into AlreadyExistsException with 'Table %s already exists in the database %s and catalog %s'.

Source

Thrown at flink/v2.3/flink/src/main/java/org/apache/iceberg/flink/FlinkDynamicTableFactory.java:226

    if (!flinkCatalog.databaseExists(catalogDatabase)) {
      try {
        flinkCatalog.createDatabase(
            catalogDatabase, new CatalogDatabaseImpl(Maps.newHashMap(), null), true);
      } catch (DatabaseAlreadyExistException e) {
        throw new AlreadyExistsException(
            e,
            "Database %s already exists in the iceberg catalog %s.",
            catalogName,
            catalogDatabase);
      }
    }

    // Create table if not exists in the external catalog.
    if (!flinkCatalog.tableExists(objectPath)) {
      try {
        flinkCatalog.createIcebergTable(objectPath, resolvedCatalogTable, true);
      } catch (TableAlreadyExistException e) {
        throw new AlreadyExistsException(
            e,
            "Table %s already exists in the database %s and catalog %s",
            catalogTable,
            catalogDatabase,
            catalogName);
      }
    }

    return TableLoader.fromCatalog(
        flinkCatalog.getCatalogLoader(), TableIdentifier.of(catalogDatabase, catalogTable));
  }

  /**
   * Merges source catalog properties (catalog name, database, table) with connector properties.
   * Source catalog name, database, table are serialized as json in FlinkCatalog#getTable to be able
   * to isolate them from iceberg table props, Here, we flatten and merge them back.
   *
   * @param tableProps the existing table properties

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Check flinkCatalog.tableExists(objectPath) first and reuse the existing table
  2. Catch AlreadyExistsException and treat as success when the existing table matches your schema
  3. Serialize table creation via deployment orchestration
  4. Use unique table names per job/environment

Example fix

// before
flinkCatalog.createIcebergTable(objectPath, resolvedCatalogTable, true);

// after
if (!flinkCatalog.tableExists(objectPath)) {
  flinkCatalog.createIcebergTable(objectPath, resolvedCatalogTable, true);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!catalog.tableExists(objectPath)) { /* create */ }

Try / catch

try { catalog.createIcebergTable(objectPath, tbl, true); } catch (TableAlreadyExistException | AlreadyExistsException e) { /* idempotent success */ }

Prevention

When it happens

Trigger: Concurrent CREATE TABLE (or Flink dynamic table source/sink initialization) for the same Iceberg table from multiple jobs; check-then-create race between tableExists and createIcebergTable.

Common situations: Two streaming jobs writing to the same table path launched simultaneously; CI pipelines deploying the same job twice; shared dev catalogs where tables are created ad hoc.

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/9de06fb776369a31. Report an issue: GitHub.