apache/iceberg · error · CommitFailedException

Requirement failed: branch or tag %s is missing, expected %s

Error message

Requirement failed: branch or tag %s is missing, expected %s

What it means

Thrown by AssertRefSnapshotId.validate when the requirement asserts that a branch or tag exists at a given snapshot ID, but the ref does not exist at all in the base metadata. This means the ref was deleted concurrently (or never existed) between when the operation was planned and when the commit validated. The commit fails with CommitFailedException and the operation must be re-planned against current state.

Source

Thrown at core/src/main/java/org/apache/iceberg/UpdateRequirement.java:123

      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;
    }

    public int lastAssignedFieldId() {
      return lastAssignedFieldId;
    }

    @Override
    public void validate(TableMetadata base) {

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Refresh table metadata and verify the ref exists before rebuilding and retrying the commit.
  2. Recreate the missing branch/tag if its removal was unintended, then retry the update.
  3. Re-plan the operation against the current table state rather than the cached snapshot reference.
  4. Check for concurrent reference cleanup jobs (ExpireSnapshots, RemoveSnapshots, ref TTL automation) colliding with writers.

Example fix

// before
table.manageSnapshots().fastForward("stage").toSnapshot(id).commit(); // 'stage' deleted concurrently
// after
table.refresh();
if (table.ref("stage") == null) {
  table.manageSnapshots().createBranch("stage").commit();
}
table.manageSnapshots().fastForward("stage").toSnapshot(id).commit();
Defensive patterns

Strategy: validation

Validate before calling

table.refresh();
Preconditions.checkArgument(table.ref("stage") != null,
    "Ref %s does not exist in current metadata", "stage");

Try / catch

try {
  commitRefOperation("stage", id);
} catch (CommitFailedException e) {
  if (e.getMessage().contains("is missing")) {
    table.refresh();
    recreateRefIfIntended();
  } else { throw e; }
}

Prevention

When it happens

Trigger: Committing AssertRefSnapshotId(name, snapshotId) with a non-null snapshotId when base.ref(name) returns null — the branch/tag was removed by ExpireSnapshots/reference removal or was never created before this commit.

Common situations: Another job drops the branch/tag while a writer holds planned operations against it; a misconfigured environment points at a table where the ref name differs or was never created; REST catalog commits where the client cached an older table version that still contained the ref.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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