prestodb/presto · warning · CommitFailedException
Cannot commit: stale table metadata for %s
Error message
Cannot commit: stale table metadata for %s
What it means
HiveTableOperations.commit implements Iceberg's optimistic concurrency: it first checks that the base metadata the writer started from equals the table's current() metadata. If the table advanced meanwhile, it throws CommitFailedException 'Cannot commit: stale table metadata for <schema.table>' so the Iceberg layer can retry the operation against fresh metadata. This is an expected conflict signal, not corruption.
Source
Thrown at presto-iceberg/src/main/java/com/facebook/presto/iceberg/HiveTableOperations.java:240
String metadataLocation = table.getParameters().get(METADATA_LOCATION);
if (metadataLocation == null) {
throw new PrestoException(ICEBERG_INVALID_METADATA, format("Table is missing [%s] property: %s", METADATA_LOCATION, getSchemaTableName()));
}
refreshFromMetadataLocation(metadataLocation);
return currentMetadata;
}
@Override
public void commit(@Nullable TableMetadata base, TableMetadata metadata)
{
requireNonNull(metadata, "metadata is null");
// if the metadata is already out of date, reject it
if (!Objects.equals(base, current())) {
throw new CommitFailedException("Cannot commit: stale table metadata for %s", getSchemaTableName());
}
// if the metadata is not changed, return early
if (Objects.equals(base, metadata)) {
return;
}
String newMetadataLocation = writeNewMetadata(metadata, version + 1);
Table table;
boolean useHMSLock = Optional.ofNullable(metadata.property(TableProperties.HIVE_LOCK_ENABLED, null))
.map(Boolean::parseBoolean)
.orElse(config.getLockingEnabled());
try (HiveMetastoreLock ignored = HiveMetastoreLock.acquire(metastore, metastoreContext, useHMSLock, database, tableName)) {
try {
if (base == null) {
String tableComment = metadata.properties().get(TABLE_COMMENT);
Map<String, String> parameters = new HashMap<>();View on GitHub (pinned to 55bb57d202)
Solutions
- Retry the operation — Iceberg's commit machinery is designed to retry on CommitFailedException with refreshed metadata.
- Reduce concurrency to the table or use separate tables/partitions to avoid overlapping writers.
- Enable/verify metastore-based locking for Hive-catalog Iceberg tables so concurrent commits are serialized properly.
- Ensure all writers use a compatible Iceberg catalog implementation so base-version checks are consistent.
Example fix
// before: no retry
ops.commit(base, metadata);
// after: retry loop on CommitFailedException
for (int attempt = 0; attempt < 4; attempt++) {
try { ops.commit(base, metadata); return; }
catch (CommitFailedException e) { base = ops.current(); }
} Defensive patterns
Strategy: retry
Validate before calling
// re-read current metadata immediately before committing to minimize the conflict window
TableMetadata fresh = ops.current();
if (!Objects.equals(base, fresh)) { base = ops.refresh(); /* rebase changes */ } Type guard
boolean canCommit(HiveTableOperations ops, TableMetadata base) { return Objects.equals(base, ops.current()); } Try / catch
try { ops.commit(base, metadata); } catch (CommitFailedException e) { TableMetadata fresh = ops.current(); /* rebase then retry with backoff, bounded attempts */ } Prevention
- Bound concurrent writers per table; queue maintenance jobs separately
- Enable proper locking in the metastore-backed catalog configuration
- Keep write transactions short to shrink the conflict window
- Treat CommitFailedException as retryable in all tooling around the table
When it happens
Trigger: Two concurrent commits to the same table (e.g. two compaction jobs, writer + maintenance), or a long-lived operation whose cached base metadata was invalidated by an external commit (Spark, Flink, another Presto worker) before commit is attempted.
Common situations: Parallel streaming writers (Flink/Spark) writing to the same table, manually running maintenance while jobs write, metastore lock issues causing missed updates, long transactions spanning another engine's commits.
Related errors
- Metadata location [%s] is not same as table metadata locatio
- The table %s.%s has been modified concurrently
- ICEBERG_COMMIT_ERROR
- UNEXPECTED_ACCUMULO_ERROR
- Not a Hive table:
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/15e0221648cb989c.
Report an issue: GitHub.