apache/iceberg · error · CommitFailedException
Requirement failed: %s %s was created concurrently
Error message
Requirement failed: %s %s was created concurrently
What it means
This CommitFailedException comes from AssertRefSnapshotId.validate when a requirement asserts that a branch or tag must NOT already exist (snapshotId == null), but the ref is present in the base table metadata. It means another committer created the branch or tag concurrently between when this operation was planned and when the commit was validated. The optimistic-concurrency requirement failed, so the commit was rejected and must be retried against the new table state.
Source
Thrown at core/src/main/java/org/apache/iceberg/UpdateRequirement.java:115
this.snapshotId = snapshotId;
}
public String refName() {
return name;
}
public Long snapshotId() {
return snapshotId;
}
@Override
public void validate(TableMetadata base) {
SnapshotRef ref = base.ref(name);
if (ref != null) {
String type = ref.isBranch() ? "branch" : "tag";
if (snapshotId == null) {
// a null snapshot ID means the ref should not exist already
throw new CommitFailedException(
"Requirement failed: %s %s was created concurrently", type, name);
} else if (snapshotId != ref.snapshotId()) {
throw new CommitFailedException(
"Requirement failed: %s %s has changed: expected id %s != %s",
type, name, snapshotId, ref.snapshotId());
}
} else if (snapshotId != null) {
throw new CommitFailedException(
"Requirement failed: branch or tag %s is missing, expected %s", name, snapshotId);
}
}
}
class AssertLastAssignedFieldId implements UpdateRequirement {
private final int lastAssignedFieldId;
public AssertLastAssignedFieldId(int lastAssignedFieldId) {
this.lastAssignedFieldId = lastAssignedFieldId;View on GitHub (pinned to 86d9c8fc54)
Solutions
- Re-read the table metadata, check whether the branch/tag already exists and is acceptable, and drop the AssertRefSnapshotId(null) requirement (or use ifNotExists-style handling) before retrying the commit.
- Retry the whole update against the refreshed base metadata so requirements are built from current state instead of stale state.
- If creation is meant to be unique, treat the exception as 'already created' success and skip the create rather than retrying it.
- Serialize ref-creation operations for the table (e.g. application-level locking or a single coordinator) if concurrent creators are frequent.
Example fix
// before
List<UpdateRequirement> reqs = UpdateRequirements.forCreateBranch("audit");
table.manageSnapshots().commit(); // fails if 'audit' exists
// after
if (table.refs().get("audit") == null) {
table.manageSnapshots().createBranch("audit").commit();
} // or catch CommitFailedException and treat as already-created Defensive patterns
Strategy: try-catch
Validate before calling
if (table.refs().get("audit") != null) {
// ref already exists — do not send AssertRefSnapshotId(name, null)
return;
} Type guard
boolean refExists(Table t, String name) { return t.refs().get(name) != null; } Try / catch
try {
table.manageSnapshots().createBranch("audit").commit();
} catch (CommitFailedException e) {
if (e.getMessage().contains("was created concurrently")) {
// treat as already-created; verify ref and continue
} else { throw e; }
} Prevention
- Always check table.refs() before issuing create-ref operations
- Use ifNotExists-style flags in engines (e.g. Spark CREATE BRANCH IF NOT EXISTS)
- Refresh table metadata immediately before committing ref changes
- Design creators to be idempotent: catch CommitFailedException and verify outcome
When it happens
Trigger: Calling UpdateRequirements.forCreateBranch(name, null) / forCreateTag (snapshotId null) via commit of UpdateTableRequirements when base.ref(name) returns non-null — i.e. the branch/tag was created by a concurrent writer before this commit validated.
Common situations: Two clients or jobs run 'ALTER TABLE ... CREATE BRANCH' or fast-forward/management API calls at the same time; a retry after CommitFailedException races with another creator; CI pipelines issuing duplicate create-ref requests against the same table.
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
- Requirement failed: %s %s has changed: expected id %s != %s
- Requirement failed: branch or tag %s is missing, expected %s
- Requirement failed: last assigned field id changed: expected
- Requirement failed: current schema changed: expected id %s !
- Requirement failed: last assigned partition id changed: expe
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/2c7ba32b73dba6ad.
Report an issue: GitHub.