apache/druid · error · DuplicateKeyException

Tried to insert a duplicate table: %s

Error message

Tried to insert a duplicate table: %s

What it means

When inserting a new catalog table into the DB, the INSERT hits a unique-constraint violation (DuplicateKeyException path after DbUtils.isDuplicateRecordException). The manager maps the raw JDBC error to this DuplicateKeyException naming the table, because a table with the same name already exists.

Source

Thrown at extensions-core/druid-catalog/src/main/java/org/apache/druid/catalog/storage/sql/SQLCatalogManager.java:167

            {
              final TableSpec spec = table.spec();
              final long updateTime = System.currentTimeMillis();
              final Update stmt = handle
                  .createStatement(statement(INSERT_TABLE))
                  .bind(SCHEMA_NAME_COL, table.id().schema())
                  .bind(TABLE_NAME_COL, table.id().name())
                  .bind(CREATION_TIME_COL, updateTime)
                  .bind(UPDATE_TIME_COL, updateTime)
                  .bind(STATE_COL, TableMetadata.TableState.ACTIVE.code())
                  .bind(TABLE_TYPE_COL, spec.type())
                  .bind(PROPERTIES_COL, JacksonUtils.toBytes(jsonMapper, spec.properties()))
                  .bind(COLUMNS_COL, JacksonUtils.toBytes(jsonMapper, spec.columns()));
              try {
                stmt.execute();
              }
              catch (UnableToExecuteStatementException e) {
                if (DbUtils.isDuplicateRecordException(e)) {
                  throw new DuplicateKeyException(
                        "Tried to insert a duplicate table: %s",
                        table.sqlName()
                  );
                } else {
                  throw e;
                }
              }
              sendAddition(table, updateTime);
              return updateTime;
            }
          }
      );
    }
    catch (CallbackFailedException e) {
      if (e.getCause() instanceof DuplicateKeyException) {
        throw (DuplicateKeyException) e.getCause();
      }
      throw e;

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Check existence first with the manager's readTable/exists API before calling createTable, or catch DuplicateKeyException and treat it as 'already created'.
  2. Use the table lock (createTable under lock when available) so concurrent creators serialize.
  3. If a retry caused it, switch create retry logic to idempotent upsert (alterTable/createTable-if-absent semantics).
  4. Inspect the DB directly (SELECT from the catalog table table) to confirm the existing row and decide whether to reuse or delete it.

Example fix

// before
manager.createTable(id, spec); // throws DuplicateKeyException if it exists

// after
if (manager.readTable(id) == null) {
  manager.createTable(id, spec);
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (manager.readTable(id) != null) {
  throw new IllegalStateException("Table already exists: " + id.sqlName());
}

Try / catch

try {
  manager.createTable(id, spec);
} catch (DuplicateKeyException e) {
  // treat as already-created; fall back to readTable
  existing = manager.readTable(id);
}

Prevention

When it happens

Trigger: Calling SQLCatalogManager.createTable (through its JDBI withHandle callback) with a TableId whose sqlName already has a row in the catalog table definition table, causing the INSERT to violate the primary/unique key.

Common situations: Client-side create raced with another process creating the same table without a lock or prior exists() check; retry logic re-running a create that already succeeded; concurrent tooling or automation double-registering a table.

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/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/a19faf1cbfafa6ff. Report an issue: GitHub.