apache/iceberg · error · ValidationException
Cannot apply non-unique WAP ID. Found multiple snapshots wit
Error message
Cannot apply non-unique WAP ID. Found multiple snapshots with WAP ID '%s'
What it means
PublishChangesProcedure promotes a staged (WAP) snapshot written with a spark.wap.id to the table's current state via cherry-pick. WAP IDs must uniquely identify one staged snapshot; if two snapshots carry the same staged WAP ID, publishing is ambiguous and this ValidationException is thrown.
Source
Thrown at spark/v4.2/spark/src/main/java/org/apache/iceberg/spark/procedures/PublishChangesProcedure.java:102
public ProcedureParameter[] parameters() {
return PARAMETERS;
}
@Override
public Iterator<Scan> call(InternalRow args) {
ProcedureInput input = new ProcedureInput(spark(), tableCatalog(), PARAMETERS, args);
Identifier tableIdent = input.ident(TABLE_PARAM);
String wapId = input.asString(WAP_ID_PARAM);
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);
});View on GitHub (pinned to 86d9c8fc54)
Solutions
- Ensure each write uses a unique spark.wap.id (generate a UUID per write job).
- Expire or delete the duplicate staged snapshots so only one has the target WAP ID, then retry publish_changes.
- List snapshots to find duplicates (SELECT * FROM table.snapshots) and publish the correct one after cleanup.
Example fix
// before
spark.conf.set("spark.wap.id", "wap-123") // reused in two jobs
// after
spark.conf.set("spark.wap.id", java.util.UUID.randomUUID().toString()) Defensive patterns
Strategy: validation
Validate before calling
val dupes = spark.table(s"$t.snapshots")
.filter(col("summary.wap.id") === wapId).count()
require(dupes <= 1, s"$wapId used by $dupes staged snapshots") Prevention
- Generate a unique spark.wap.id (UUID) per write job
- Publish staged snapshots promptly before reuse
- Audit snapshots table for duplicate wap.id values
When it happens
Trigger: Calling call publish_changes(table => ..., wap_id => 'X') when table.snapshots() contains more than one snapshot whose staged WAP ID equals 'X'.
Common situations: Reusing the same spark.wap.id across multiple writes without publishing in between; a retry job that re-wrote data with the same hard-coded WAP ID.
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 apply non-unique WAP ID. Found multiple snapshots wit
- Cannot apply unknown WAP ID '${wapId}'
- Cannot apply unknown WAP ID '%s'
- Cannot process unknown snapshot operation: ${op.toLowerCase(
- Cannot apply non-unique WAP ID. Found multiple snapshots wit
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/cf31ebcde261cfed.
Report an issue: GitHub.