apache/iceberg · warning · CommitFailedException
Cannot commit : metadata location has changed from
Error message
Cannot commit %s: metadata location %s has changed from %s
What it means
validateMetadataLocation compares the catalog's current METADATA_LOCATION_PROP with the metadata file location of the client's base snapshot. On mismatch it throws CommitFailedException: the table changed elsewhere between this client's refresh and its commit, so applying the update would clobber someone else's commit. Callers are expected to refresh and retry.
Solutions
- Refresh before commit: table.refresh() (or reload via catalog.loadTable) so base matches the catalog, then reapply and commit — Iceberg's built-in commit retry does this; avoid caching Table objects across commits
- Increase retry attempts on the commit (CommitState / Tasks.retry with onlyRetryOn(CommitFailedException))
- Stop external edits to jdbc_tables.metadata_location; do all changes through the catalog API
- If retries keep failing, serialize writers or partition workloads so fewer writers target the same table
Example fix
// before Table cached = catalog.loadTable(ident); // long-lived // ... hours later ... cached.newDelete().deleteFromRowFilter(expr).commit(); // stale base -> CommitFailedException // after Table fresh = catalog.loadTable(ident); // reload right before each commit fresh.newDelete().deleteFromRowFilter(expr).commit();
Defensive patterns
Strategy: retry
Validate before calling
table.refresh(); // reload so base.metadataFileLocation() matches current catalog state
Try / catch
Tasks.foreach(table).retry(20).onlyRetryOn(CommitFailedException.class).run(t -> t.newAppend().appendFile(f).commit());
Prevention
- Never cache Table instances across long job lifetimes; reload before each commit
- Rely on Iceberg's automatic CommitFailedException retry loop
- Prohibit manual edits to jdbc_tables.metadata_location
- Reduce concurrent writers per table via scheduling or table partitioning
When it happens
Trigger: Another engine/job committed to the table between loadTable/refresh and commit; a table was dropped and recreated so the location changed; manual manipulation of the metadata_location column; stale Table instance cached across a long-running job and committed at the end.
Common situations: Long-running Spark/Flink jobs holding a stale Table handle while streaming writers commit continuously; multiple writers without Iceberg's retry loop; operator manually repairing catalog rows in the DB.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Failed to update table
- Cannot commit : metadata location has changed from
- Failed to create table
- Failed to update view
- An error occurred reloading table
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/0e754634fe7693cc.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/apache/iceberg/jdbc/JdbcTableOperations.java:205
catalogName,
namespace,
tableIdentifier,
newMetadataLocation);
if (insertRecord == 1) {
LOG.debug("Successfully committed to new table: {}", tableIdentifier);
} else {
throw new CommitFailedException(
"Failed to create table %s in catalog %s", tableIdentifier, catalogName);
}
}
private void validateMetadataLocation(Map<String, String> table, TableMetadata base) {
String catalogMetadataLocation = table.get(METADATA_LOCATION_PROP);
String baseMetadataLocation = base != null ? base.metadataFileLocation() : null;
if (!Objects.equals(baseMetadataLocation, catalogMetadataLocation)) {
throw new CommitFailedException(
"Cannot commit %s: metadata location %s has changed from %s",
tableIdentifier, baseMetadataLocation, catalogMetadataLocation);
}
}
@Override
public FileIO io() {
return fileIO;
}
@Override
protected String tableName() {
return tableIdentifier.toString();
}
}
View on GitHub (pinned to 86d9c8fc54)