apache/iceberg · error · org.apache.flink.table.catalog.exceptions.AlreadyExistsException
Table already exists in the database and catalog
Error message
Table %s already exists in the database %s and catalog %s
What it means
During CREATE TABLE handling, FlinkDynamicTableFactory.createTableLoader tries to create the Iceberg table if it does not exist. If the creation races with another writer (or the existence check is stale), Flink throws TableAlreadyExistException which is wrapped as AlreadyExistsException with this message. It means the requested iceberg table already exists in the database/catalog.
Solutions
- Check whether the table already exists (SHOW TABLES / tableExists) before creating, or make creation idempotent.
- Catch AlreadyExistsException and treat it as success if your workflow expects the table to potentially exist.
- Create the table once out-of-band (e.g. via Spark/iceberg DDL) and have Flink only read/write it.
Example fix
// before
flinkCatalog.createIcebergTable(objectPath, resolvedCatalogTable, true);
// after
if (!flinkCatalog.tableExists(objectPath)) {
try { flinkCatalog.createIcebergTable(objectPath, resolvedCatalogTable, true); }
catch (TableAlreadyExistException e) { /* treat as success */ }
} Defensive patterns
Strategy: try-catch
Validate before calling
if (flinkCatalog.tableExists(new ObjectPath(databaseName, tableName))) {
LOG.info("Table {}.{} already exists; skipping create", databaseName, tableName);
} Type guard
null
Try / catch
try {
submitCreateTable();
} catch (AlreadyExistsException e) {
LOG.info("Table already exists; treating as success", e);
} Prevention
- Use idempotent DDL practices (check existence or catch already-exists).
- Create Iceberg tables out-of-band for shared environments; let Flink only read/write.
- Deduplicate DDL submissions in CI/CD pipelines.
When it happens
Trigger: Submitting a CREATE TABLE (or a source/sink DDL with implicit create) for a table that already exists in the Iceberg catalog; two concurrent jobs creating the same table; ignoreIfExists semantics colliding.
Common situations: Re-running the same Flink SQL statement twice; CI pipelines submitting DDL idempotently without IF NOT EXISTS; multiple executors racing during job startup.
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
- Can not alter the default database when the iceberg catalog…
- Can not alter the default database when the iceberg catalog…
- Can not alter the default database when the iceberg catalog…
- Cannot create table as it already exists
- Cannot create table as the namespace does not exist
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/d981307b718514ba.
Report an issue: GitHub.
Appendix: source
Thrown at flink/v1.20/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 propertiesView on GitHub (pinned to 86d9c8fc54)