apache/iceberg · error · ValidationException

Cannot apply unknown WAP ID '%s'

Error message

Cannot apply unknown WAP ID '%s'

What it means

PublishChangesProcedure looks up a staged snapshot whose summary 'wap.id' equals the supplied wap_id argument. If no snapshot in the table's history matches, it throws this ValidationException: there is nothing staged to publish under that ID. Publishing is skipped and the table is left unchanged.

Source

Thrown at spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/procedures/PublishChangesProcedure.java:112

    return modifyIcebergTable(
        tableIdent,
        table -> {
          Snapshot matchingSnap = null;
          for (Snapshot snap : table.snapshots()) {
            if (wapId.equals(WapUtil.stagedWapId(snap))) {
              if (matchingSnap != null) {
                throw new ValidationException(
                    "Cannot apply non-unique WAP ID. Found multiple snapshots with WAP ID '%s'",
                    wapId);
              } else {
                matchingSnap = snap;
              }
            }
          }

          if (matchingSnap == null) {
            throw new ValidationException("Cannot apply unknown WAP ID '%s'", wapId);
          }

          long wapSnapshotId = matchingSnap.snapshotId();
          table.manageSnapshots().cherrypick(wapSnapshotId).commit();
          Snapshot currentSnapshot = table.currentSnapshot();
          InternalRow outputRow = newInternalRow(wapSnapshotId, currentSnapshot.snapshotId());
          return asScanIterator(OUTPUT_TYPE, outputRow);
        });
  }

  @Override
  public String name() {
    return NAME;
  }

  @Override
  public String description() {
    return "ApplyWapChangesProcedure";

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Check staged snapshots exist: SELECT * FROM db.t.snapshots and inspect summary['wap.id'].
  2. Confirm the writer ran with spark.wap.id set before re-running publish_changes with the exact same value.
  3. Pass the exact wap.id string used at write time (watch whitespace/case).
  4. If already published, no action needed — the error is expected for a second publish attempt.

Example fix

-- before
CALL iceberg.system.publish_changes(table => 'db.t', wap_id => '1234');
-- after (use id verified in snapshots metadata)
SELECT snapshot_id, summary['wap.id'] FROM db.t.snapshots;
CALL iceberg.system.publish_changes(table => 'db.t', wap_id => '1234-abcd');
Defensive patterns

Strategy: validation

Validate before calling

assert table.snapshots().stream().anyMatch(s -> wapId.equals(s.summary().get("wap.id"))) : "no staged snapshot for wap.id";

Try / catch

try { publishChanges(table, wapId); } catch (ValidationException e) { /* unknown WAP ID: verify staged write committed */ }

Prevention

When it happens

Trigger: CALL publish_changes(table => 'db.t', wap_id => 'X') where no snapshot summary contains wap.id=X — the staged write never happened, the snapshot was already published or expired, or the passed ID doesn't match the writer's ID.

Common situations: Calling publish before the staging write committed; typos or case mismatch in the wap_id string; the staged snapshot was removed by expire_snapshots; running publish against a table that was written without WAP mode enabled (spark.wap.id unset).

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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