apache/iceberg · warning · CommitFailedException

Failed to create table %s in catalog %s

Error message

Failed to create table %s in catalog %s

What it means

After the existence pre-checks pass, createTable performs the INSERT of the new catalog row. If the INSERT affects any number of rows other than 1 (almost always 0), createTable throws CommitFailedException because the table could not be registered. This is another optimistic-concurrency failure point: the INSERT can silently match 0 rows under concurrent modification or DB-specific behaviors.

Source

Thrown at core/src/main/java/org/apache/iceberg/jdbc/JdbcTableOperations.java:195

    }

    if (JdbcUtil.tableExists(schemaVersion, catalogName, connections, tableIdentifier)) {
      throw new AlreadyExistsException("Table already exists: %s", tableIdentifier);
    }

    int insertRecord =
        JdbcUtil.doCommitCreateTable(
            schemaVersion,
            connections,
            catalogName,
            namespace,
            tableIdentifier,
            newMetadataLocation);

    if (insertRecord == 1) {
      LOG.debug("Successfully committed to new table: {}", tableIdentifier);
    } else {
      throw new CommitFailedException(
          "Failed to create table %s in catalog %s", tableIdentifier, catalogName);
    }
  }

  private void validateMetadataLocation(Map<String, String> table, TableMetadata base) {
    String catalogMetadataLocation = table.get(METADATA_LOCATION_PROP);
    String baseMetadataLocation = base != null ? base.metadataFileLocation() : null;

    if (!Objects.equals(baseMetadataLocation, catalogMetadataLocation)) {
      throw new CommitFailedException(
          "Cannot commit %s: metadata location %s has changed from %s",
          tableIdentifier, baseMetadataLocation, catalogMetadataLocation);
    }
  }

  @Override
  public FileIO io() {
    return fileIO;

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Retry the whole create: treat CommitFailedException as retryable — recheck tableExists and re-attempt createTable
  2. Verify connection autocommit settings so INSERT row counts reflect real commits
  3. Inspect jdbc_tables constraints/triggers that could make the INSERT no-op
  4. Fall back to loadTable if a concurrent creator won the race and the existing table is acceptable

Example fix

// before
catalog.createTable(ident, schema); // rare CommitFailedException aborts pipeline
// after
try {
  catalog.createTable(ident, schema);
} catch (CommitFailedException e) {
  if (!catalog.tableExists(ident)) {
    catalog.createTable(ident, schema); // retry once; else someone else created it
  }
}
Defensive patterns

Strategy: retry

Validate before calling

if (!catalog.tableExists(identifier)) { /* safe window is small; still retry on failure */ }

Try / catch

try { catalog.createTable(identifier, schema); } catch (CommitFailedException e) { if (!catalog.tableExists(identifier)) { catalog.createTable(identifier, schema); } }

Prevention

When it happens

Trigger: Concurrent drop of the just-validated table before INSERT; DB triggers/policies suppressing the insert; connection in a failed transaction state so the UPDATE/INSERT count reports 0; a race where another creator inserted between tableExists() and doCommitCreateTable().

Common situations: Very high create/delete churn on the same names; autocommit misconfiguration in the connection pool; DB-level constraints beyond the unique key (e.g. foreign key to namespaces in strict setups).

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/144f394c5207bc0c. Report an issue: GitHub.