apache/iceberg · error · AlreadyExistsException

Cannot rename %s to %s. Table already exists

Error message

Cannot rename %s to %s. Table already exists

What it means

renameTableOrView() throws AlreadyExistsException when a table already exists at the rename destination identifier. Hive requires unique table names per database, so renaming onto an existing table would clobber it and is refused.

Source

Thrown at hive-metastore/src/main/java/org/apache/iceberg/hive/HiveCatalog.java:406

        .collect(Collectors.toList());
  }

  @SuppressWarnings("checkstyle:CyclomaticComplexity")
  private void renameTableOrView(
      TableIdentifier from,
      TableIdentifier originalTo,
      HiveOperationsBase.ContentType contentType) {
    Preconditions.checkArgument(isValidIdentifier(from), "Invalid identifier: %s", from);

    TableIdentifier to = removeCatalogName(originalTo);
    Preconditions.checkArgument(isValidIdentifier(to), "Invalid identifier: %s", to);
    if (!namespaceExists(to.namespace())) {
      throw new NoSuchNamespaceException(
          "Cannot rename %s to %s. Namespace does not exist: %s", from, to, to.namespace());
    }

    if (tableExists(to)) {
      throw new AlreadyExistsException("Cannot rename %s to %s. Table already exists", from, to);
    }

    if (viewExists(to)) {
      throw new AlreadyExistsException("Cannot rename %s to %s. View already exists", from, to);
    }

    String toDatabase = to.namespace().level(0);
    String fromDatabase = from.namespace().level(0);
    String fromName = from.name();

    try {
      Table table = clients.run(client -> client.getTable(fromDatabase, fromName));
      validateTableIsIcebergTableOrView(contentType, table, CatalogUtil.fullTableName(name, from));

      table.setDbName(toDatabase);
      table.setTableName(to.name());

      clients.run(

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Check catalog.tableExists(to) before renaming and pick a free destination name.
  2. Drop or rename the conflicting destination table if it is stale and safe to remove.
  3. Make destination names unique (add timestamp/suffix) in concurrent workflows.
  4. Beware Hive's case-insensitive matching — check variants of the destination name.

Example fix

// before
catalog.renameTable(from, TableIdentifier.of(db, "events"));
// after
TableIdentifier to = TableIdentifier.of(db, "events");
if (catalog.tableExists(to)) {
  throw new IllegalStateException("Destination already exists: " + to);
}
catalog.renameTable(from, to);
Defensive patterns

Strategy: validation

Validate before calling

TableIdentifier to = TableIdentifier.of(db, "events");
if (catalog.tableExists(to)) {
  throw new IllegalStateException("Rename target already exists: " + to);
}
catalog.renameTable(from, to);

Try / catch

try {
  catalog.renameTable(from, to);
} catch (AlreadyExistsException e) {
  // pick a new destination name or resolve the conflict manually
  throw e;
}

Prevention

When it happens

Trigger: Calling catalog.renameTable(from, to) (or renameView, which shares this path) where a table named to.name() already exists in to.namespace().

Common situations: Renaming to a name that was previously used and never cleaned up; concurrent job created the destination table first; retrying a rename that already partially succeeded elsewhere; case-insensitivity surprises since Hive matches names case-insensitively.

Related errors


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