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 '${wapId}' What it means
The publish_changes procedure cherry-picks a staged (WAP) snapshot identified by its snap.wap-id property. Publishing must be deterministic, so if more than one snapshot carries the same wap-id, ValidationException is thrown because it would be ambiguous which staged snapshot to publish.
Source
Thrown at spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/procedures/PublishChangesProcedure.java:97
public StructType outputType() {
return OUTPUT_TYPE;
}
@Override
public InternalRow[] 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 new InternalRow[] {outputRow};
});View on GitHub (pinned to 86d9c8fc54)
Solutions
- Generate a unique spark.wap.id per write job (e.g. UUID.randomUUID()) before staging.
- Inspect tbl.snapshots for duplicate wap-id values and expire stale staged snapshots.
- Re-run publish only after a single snapshot holds the target wap-id.
- Enforce in orchestration that concurrent writers never share WAP IDs.
Example fix
// before
spark.conf.set("spark.wap.id", "fixed-id")
// after
spark.conf.set("spark.wap.id", java.util.UUID.randomUUID().toString()) Defensive patterns
Strategy: validation
Validate before calling
long matches = spark.read().format("iceberg").load("cat.db.t.snapshots")
.filter("snapshot_props['wap-id'] = '" + wapId + "'").count();
if (matches > 1) throw new IllegalStateException("Non-unique WAP ID: " + wapId); Try / catch
try { spark.sql("CALL cat.system.publish_changes(wap_id => '" + wapId + "')"); } catch (ValidationException e) { if (e.getMessage().contains("non-unique WAP ID")) { /* deduplicate staged snapshots or use a fresh unique WAP ID */ } throw e; } Prevention
- Generate a unique spark.wap.id (UUID) per write job.
- Never hardcode or share WAP IDs across concurrent jobs.
- Expire stale staged snapshots before republishing.
- Audit duplicate wap-id values in the snapshots metadata table.
When it happens
Trigger: Two writes staged snapshots with the same `spark.wap.id` into the same table, then `CALL cat.system.publish_changes(wap_id => 'dup-id')`.
Common situations: Reusing a fixed WAP ID from config across runs; concurrent CI pipelines sharing a WAP ID; missing per-job UUID generation.
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 unknown WAP ID '${wapId}'
- Cannot apply non-unique WAP ID. Found multiple snapshots wit
- Cannot apply unknown WAP ID '%s'
- Cannot apply non-unique WAP ID. Found multiple snapshots wit
- Cannot apply unknown WAP ID '%s'
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/4b025422df6ca2dc.
Report an issue: GitHub.