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

  1. Retry the operation — Iceberg's commit machinery is designed to retry on CommitFailedException with refreshed metadata.
  2. Reduce concurrency to the table or use separate tables/partitions to avoid overlapping writers.
  3. Enable/verify metastore-based locking for Hive-catalog Iceberg tables so concurrent commits are serialized properly.
  4. 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

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


AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04). Data as JSON: /api/errors/15e0221648cb989c. Report an issue: GitHub.