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

  1. 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.
  2. Retry the whole update against the refreshed base metadata so requirements are built from current state instead of stale state.
  3. If creation is meant to be unique, treat the exception as 'already created' success and skip the create rather than retrying it.
  4. 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

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


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