apache/iceberg · error · AlreadyExistsException

Table already exists: %s

Error message

Table already exists: %s

What it means

CachingCatalog.create() uses an atomic compute-if-absent over the cache to build the table; if the table was already created by another thread/committer between the caller's request and creation (created flag stays false), the catalog throws AlreadyExistsException. This is the standard 409-style 'already exists' outcome for concurrent creation.

Source

Thrown at core/src/main/java/org/apache/iceberg/CachingCatalog.java:275

    @Override
    public TableBuilder withProperty(String key, String value) {
      innerBuilder.withProperty(key, value);
      return this;
    }

    @Override
    public Table create() {
      AtomicBoolean created = new AtomicBoolean(false);
      Table table =
          tableCache.get(
              canonicalizeIdentifier(ident),
              identifier -> {
                created.set(true);
                return innerBuilder.create();
              });

      if (!created.get()) {
        throw new AlreadyExistsException("Table already exists: %s", ident);
      }

      return table;
    }

    @Override
    public Transaction createTransaction() {
      // create a new transaction without altering the cache. the table doesn't exist until the
      // transaction is
      // committed. if the table is created before the transaction commits, any cached version is
      // correct and the
      // transaction create will fail. if the transaction commits before another create, then the
      // cache will be empty.
      return innerBuilder.createTransaction();
    }

    @Override
    public Transaction replaceTransaction() {

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Treat AlreadyExistsException as success in idempotent create workflows
  2. Fall back to catalog.loadTable(ident) when create fails with already-exists
  3. Coordinate table creation in a single setup step/job before writers start
  4. Use createOrReplaceTransaction if replacement is intended

Example fix

// before
Table t = catalog.createTable(ident, schema);
// after
Table t;
try {
  t = catalog.createTable(ident, schema);
} catch (AlreadyExistsException e) {
  t = catalog.loadTable(ident);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (catalog.tableExists(ident)) { return catalog.loadTable(ident); } // still racy; keep the catch

Try / catch

catch (AlreadyExistsException e) { return catalog.loadTable(ident); }

Prevention

When it happens

Trigger: Calling catalog.createTable(ident, ...) or the table builder's create() concurrently from two jobs/threads with the same identifier; a retrying job re-attempting creation after a prior partial success.

Common situations: Two Spark/Flink jobs creating the same table at startup; idempotency retries racing with the first successful create; multi-cluster writers against a shared catalog.

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