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
- Refresh the table (re-read latest metadata) and reapply/redo the operation, then commit again
- Use row-level conflict detection/merge for overlapping writes
- 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
- Refresh right before commit in long-running jobs
- Minimize time between load and commit
- Coordinate concurrent writers with locks; check base-vs-current metadata before committing
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
- Cannot commit %s: concurrent update detected
- Cannot find default warehouse location: namespace %s does no
- Cannot create namespace %s: already exists
- Cannot find namespace %s
- Cannot delete non-empty namespace %s
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/0772503a92e4972f.
Report an issue: GitHub.