apache/iceberg · error · AlreadyExistsException

Table already exists: %s

Error message

Table already exists: %s

What it means

AlreadyExistsException from BigQueryMetastoreClientImpl.internalCreate when the BigQuery tables.insert reports the table already exists. The original AlreadyExistsException is chained as the cause and the message includes the full Table object.

Source

Thrown at bigquery/src/main/java/org/apache/iceberg/gcp/bigquery/BigQueryMetastoreClientImpl.java:363

    return response;
  }

  private Table internalCreate(Table table) {
    try {
      HttpResponse response =
          client
              .tables()
              .insert(
                  Preconditions.checkNotNull(table.getTableReference()).getProjectId(),
                  Preconditions.checkNotNull(table.getTableReference()).getDatasetId(),
                  table)
              .executeUnparsed();
      return convertExceptionIfUnsuccessful(response).parseAs(Table.class);
    } catch (IOException e) {
      throw new RuntimeIOException("%s", e);
    } catch (AlreadyExistsException e) {
      throw new AlreadyExistsException(e, "Table already exists: %s", table);
    }
  }

  @Override
  public Table load(TableReference tableReference) {
    try {
      HttpResponse response =
          client
              .tables()
              .get(
                  tableReference.getProjectId(),
                  tableReference.getDatasetId(),
                  tableReference.getTableId())
              .executeUnparsed();
      if (response.getStatusCode() == HttpStatusCodes.STATUS_CODE_NOT_FOUND) {
        throw new NoSuchTableException("%s", response.getStatusMessage());
      }

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Check tableExists(identifier) before creating, or use catalog.createOrReplaceTable when replacement is intended
  2. Catch AlreadyExistsException and treat as success for idempotent setup
  3. Add randomness/locking for concurrent creators of the same table name
  4. Verify you are targeting the intended dataset/project (name collision across environments)

Example fix

// before
catalog.createTable(ident, schema);
// after
if (!catalog.tableExists(ident)) {
  catalog.createTable(ident, schema);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (!catalog.tableExists(ident)) { catalog.createTable(ident, schema); }

Try / catch

try { catalog.createTable(ident, schema); } catch (AlreadyExistsException e) { /* exists; skip or replace */ }

Prevention

When it happens

Trigger: Calling catalog.createTable / client.create for a TableIdentifier whose table already exists in the dataset; concurrent createTable races for the same identifier; createOrReplace fallback that catches this to switch to replace.

Common situations: CREATE TABLE without IF NOT EXISTS semantics in scripts, two Spark jobs creating the same table concurrently, rerunning schema-initialization jobs.

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