apache/iceberg · error · AlreadyExistsException
Table already exists: %s
Error message
Table already exists: %s
What it means
When commit() is called with base == null (a table-creation commit) but current() is non-null, the table already exists, so creation is rejected with AlreadyExistsException. The reference comparison base != current() distinguishes create (base null) from update (base non-null) and routes each failure to its specific error.
Source
Thrown at core/src/main/java/org/apache/iceberg/BaseMetastoreTableOperations.java:117
throw e;
}
return current();
}
protected void doRefresh() {
throw new UnsupportedOperationException("Not implemented: doRefresh");
}
@Override
public void commit(TableMetadata base, TableMetadata metadata) {
// if the metadata is already out of date, reject it
if (base != current()) {
if (base != null) {
throw new CommitFailedException("Cannot commit: stale table metadata");
} else {
// when current is non-null, the table exists. but when base is null, the commit is trying
// to create the table
throw new AlreadyExistsException("Table already exists: %s", tableName());
}
}
// if the metadata is not changed, return early
if (base == metadata) {
LOG.info("Nothing to commit.");
return;
}
long start = System.currentTimeMillis();
doCommit(base, metadata);
CatalogUtil.deleteRemovedMetadataFiles(io(), base, metadata);
requestRefresh();
LOG.info(
"Successfully committed to table {} in {} ms",
tableName(),
System.currentTimeMillis() - start);
}View on GitHub (pinned to 86d9c8fc54)
Solutions
- Catch AlreadyExistsException and load the existing table instead of creating
- Call refresh() and verify current() == null before attempting a creation commit
- Use catalog.buildTable(...).create(), which wraps this logic with clearer errors
- Ensure only one writer performs creation DDL for a given identifier
Example fix
// before
ops.commit(null, metadata); // may throw if table now exists
// after
ops.refresh();
if (ops.current() != null) {
table = new BaseTable(ops, name); // already created by someone else
} else {
try { ops.commit(null, metadata); }
catch (AlreadyExistsException e) { table = new BaseTable(ops, name); }
} Defensive patterns
Strategy: try-catch
Validate before calling
ops.refresh(); if (ops.current() != null) { /* table exists; don't attempt creation commit */ } Try / catch
try { ops.commit(null, metadata); }
catch (AlreadyExistsException e) { /* concurrent creation won; load existing table */ } Prevention
- Never assume a pre-check of absence stays valid; re-check at commit
- Use catalog create APIs that handle the race
- Limit creation DDL to a single writer
When it happens
Trigger: Calling ops.commit(null, metadata) to create a table that was registered in the metastore after the caller last checked, or calling commit(null, ...) on operations for an existing table without refreshing.
Common situations: Lost creation race between concurrent DDL writers; reusing TableOperations for a table that already exists when attempting create-style commits; catalog pre-check (ops.current() == null) that became stale before commit.
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
- Table %s already exists in the database %s and catalog %s
- Table was created concurrently: %s
- Table already exists: %s
- View already exists: %s
- Database %s already exists in the iceberg catalog %s.
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/5e419354856069d2.
Report an issue: GitHub.