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

Database %s already exists in the iceberg catalog %s.

Error message

Database %s already exists in the iceberg catalog %s.

What it means

When FlinkDynamicTableFactory.createTableLoader initializes an Iceberg table, it auto-creates the database in the external catalog if missing. If FlinkCatalog.createDatabase still reports DatabaseAlreadyExistException (a race or a misreported existence check), it is rethrown as Flink's AlreadyExistsException with this message. It signals a concurrent creation or a stale existence check.

Source

Thrown at flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/FlinkDynamicTableFactory.java:213

    String catalogDatabase = flinkConf.get(FlinkCreateTableOptions.CATALOG_DATABASE, databaseName);
    Preconditions.checkArgument(
        catalogDatabase != null,
        "Invalid database name: null. Set %s create table option or specify fully qualified table name.",
        FlinkCreateTableOptions.CATALOG_DATABASE);

    String catalogTable = flinkConf.get(FlinkCreateTableOptions.CATALOG_TABLE, tableName);

    FlinkCatalog flinkCatalog = createCatalogLoader(mergedProps, catalogName);
    ObjectPath objectPath = new ObjectPath(catalogDatabase, catalogTable);

    // Create database if not exists in the external catalog.
    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);

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Rerun the job — after the first creation succeeds, the existence check will pass and the error disappears.
  2. Serialize database creation: create the database explicitly once (e.g. via catalog DDL or the Iceberg catalog API) before launching concurrent jobs.
  3. Add retry-on-already-exists handling in your launcher so a race is treated as success.

Example fix

// before
flinkCatalog.createDatabase(catalogDatabase, new CatalogDatabaseImpl(Maps.newHashMap(), null), true);
// after
try {
  flinkCatalog.createDatabase(catalogDatabase, new CatalogDatabaseImpl(Maps.newHashMap(), null), true);
} catch (AlreadyExistsException e) {
  // ignore — database created concurrently
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!flinkCatalog.databaseExists(databaseName)) {
  LOG.info("Creating iceberg database {}", databaseName);
}

Type guard

null

Try / catch

try {
  createDatabasesAndTables();
} catch (AlreadyExistsException e) {
  LOG.warn("Database created concurrently; continuing", e);
}

Prevention

When it happens

Trigger: Two jobs/statements creating the same iceberg database concurrently so databaseExists returns false but createDatabase collides; flinkCatalog.databaseExists returning false inconsistently.

Common situations: Parallel Flink SQL client sessions issuing CREATE CATALOG/DDL against the same iceberg catalog at once; retry storms after a failed submission.

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