apache/iceberg · error · CommitFailedException

Cannot commit: stale view metadata

Error message

Cannot commit: stale view metadata

What it means

BaseViewOperations.commit compares the commit's base metadata against the current in-memory/current metadata by reference. When base is non-null but differs from current, the view was modified by someone else and this commit's optimistic concurrency check fails with a CommitFailedException so the caller can retry.

Source

Thrown at core/src/main/java/org/apache/iceberg/view/BaseViewOperations.java:111

        shouldRefresh = true;
      }

      currentMetadata = null;
      currentMetadataLocation = null;
      version = -1;
      throw e;
    }

    return current();
  }

  @Override
  @SuppressWarnings("ImmutablesReferenceEquality")
  public void commit(ViewMetadata base, ViewMetadata metadata) {
    // if the metadata is already out of date, reject it
    if (base != current()) {
      if (base != null) {
        throw new CommitFailedException("Cannot commit: stale view metadata");
      } else {
        // when current is non-null, the view exists. but when base is null, the commit is trying
        // to create the view
        throw new AlreadyExistsException("View already exists: %s", viewName());
      }
    }

    // if the metadata is not changed, return early
    if (base == metadata) {
      LOG.info("Nothing to commit.");
      return;
    }

    long start = System.currentTimeMillis();
    doCommit(base, metadata);
    requestRefresh();

    LOG.info(

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Refresh the view (reload metadata) and retry the commit
  2. Wrap the update in a retry loop that re-reads the view on CommitFailedException
  3. Serialize view updates through a single writer or lock
  4. Shorten the window between reading and committing view metadata

Example fix

// before
ops.commit(staleBase, newMetadata);
// after
Tasks.foreach(newMetadata)
    .retry(3)
    .exponentialBackoff(100, 10000)
    .suppressExceptions()
    .run(m -> {
      ViewMetadata base = ((BaseViewOperations) ops).current();
      ops.commit(base, m);
    });
Defensive patterns

Strategy: retry

Validate before calling

ViewMetadata current = ((BaseViewOperations) ops).current(); boolean stale = (base != current);

Try / catch

Tasks.foreach(metadata).retry(3).exponentialBackoff(100, 10000).run(m -> ops.commit(((BaseViewOperations) ops).current(), m));

Prevention

When it happens

Trigger: A view operation built on stale metadata commits while another client has already updated the view; the internal metadata cache was refreshed between read and commit.

Common situations: Long-running view update transactions racing with external updates; two Spark sessions altering the same view; automation pipelines updating view SQL concurrently.

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


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