apache/iceberg · error · CommitFailedException

Cannot commit %s because base metadata location '%s' is not

Error message

Cannot commit %s because base metadata location '%s' is not same as the current DynamoDb location '%s'

What it means

CommitFailedException thrown by checkMetadataLocation when the TableMetadata base the client committed from has a metadataFileLocation different from the metadata location currently stored in DynamoDB. This is optimistic-concurrency validation: the table was changed by someone else after this client's refresh.

Source

Thrown at aws/src/main/java/org/apache/iceberg/aws/dynamodb/DynamoDbTableOperations.java:169

          throw new CommitStateUnknownException(persistFailure);
      }
    } finally {
      try {
        if (commitStatus == CommitStatus.FAILURE) {
          // if anything went wrong, clean up the uncommitted metadata file
          io().deleteFile(newMetadataLocation);
        }
      } catch (RuntimeException e) {
        LOG.error("Failed to cleanup metadata file at {}", newMetadataLocation, e);
      }
    }
  }

  private void checkMetadataLocation(GetItemResponse table, TableMetadata base) {
    String dynamoMetadataLocation = table.hasItem() ? getMetadataLocation(table) : null;
    String baseMetadataLocation = base != null ? base.metadataFileLocation() : null;
    if (!Objects.equals(baseMetadataLocation, dynamoMetadataLocation)) {
      throw new CommitFailedException(
          "Cannot commit %s because base metadata location '%s' is not same as the current DynamoDb location '%s'",
          tableName(), baseMetadataLocation, dynamoMetadataLocation);
    }
  }

  private String getMetadataLocation(GetItemResponse table) {
    return table.item().get(DynamoDbCatalog.toPropertyCol(METADATA_LOCATION_PROP)).s();
  }

  private Map<String, String> prepareProperties(
      GetItemResponse response, String newMetadataLocation) {
    Map<String, String> properties =
        response.hasItem() ? getProperties(response) : Maps.newHashMap();
    properties.put(TABLE_TYPE_PROP, ICEBERG_TABLE_TYPE_VALUE.toUpperCase(Locale.ROOT));
    properties.put(METADATA_LOCATION_PROP, newMetadataLocation);
    if (currentMetadataLocation() != null && !currentMetadataLocation().isEmpty()) {
      properties.put(PREVIOUS_METADATA_LOCATION_PROP, currentMetadataLocation());
    }

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Refresh the table (re-read latest metadata) and reapply/redo the operation, then commit again
  2. Use row-level conflict detection/merge for overlapping writes
  3. Coordinate concurrent writers with a lock (e.g. DynamoDbLockManager) to serialize commits

Example fix

// before
Table t = catalog.loadTable(id);
// long-running work, others commit meanwhile
t.updateSchema().addColumn("c", Types.StringType.get()).commit(); // fails
// after
Table t = catalog.loadTable(id);
// ... long work ...
t.refresh(); // rebase before committing
t.updateSchema().addColumn("c", Types.StringType.get()).commit();
Defensive patterns

Strategy: retry

Validate before calling

table.refresh(); // ensure base metadata is current before any commit

Try / catch

try {
  table.updateSchema().addColumn("c", Types.StringType.get()).commit();
} catch (CommitFailedException e) {
  table.refresh(); // rebase and retry
  table.updateSchema().addColumn("c", Types.StringType.get()).commit();
}

Prevention

When it happens

Trigger: doCommit fetches the current Dynamo item and compares its COL_METADATA_LOCATION against the client's base metadata; a mismatch throws.

Common situations: Another writer committed between this client's loadTable/refresh and its commit; a long batch job holding stale metadata; a concurrent compaction or expireSnapshots job.

Related errors


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