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
- Refresh the view (reload metadata) and retry the commit
- Wrap the update in a retry loop that re-reads the view on CommitFailedException
- Serialize view updates through a single writer or lock
- 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
- Always re-read current metadata immediately before commit
- Use Tasks.foreach retry loops for optimistic-concurrency commits
- Avoid holding view metadata for long periods before committing
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
- Cannot commit: stale table metadata
- Cannot commit %s because Glue detected concurrent update
- Requirement failed: table already exists
- Requirement failed: %s %s was created concurrently
- Requirement failed: %s %s has changed: expected id %s != %s
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/183d856d0baad30f.
Report an issue: GitHub.