{"record":{"id":"3e0d7c2a25fb64fb","repo":"apache/iceberg","slug":"cannot-apply-non-unique-wap-id-found-multiple-sna-3e0d7c","errorCode":null,"errorMessage":"Cannot apply non-unique WAP ID. Found multiple snapshots with WAP ID '%s'","messagePattern":"Cannot apply non-unique WAP ID\\. Found multiple snapshots with WAP ID '(.+?)'","errorType":"validation","errorClass":"ValidationException","httpStatus":null,"severity":"error","filePath":"spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/procedures/PublishChangesProcedure.java","lineNumber":102,"sourceCode":"  public ProcedureParameter[] parameters() {\n    return PARAMETERS;\n  }\n\n  @Override\n  public Iterator<Scan> call(InternalRow args) {\n    ProcedureInput input = new ProcedureInput(spark(), tableCatalog(), PARAMETERS, args);\n\n    Identifier tableIdent = input.ident(TABLE_PARAM);\n    String wapId = input.asString(WAP_ID_PARAM);\n\n    return modifyIcebergTable(\n        tableIdent,\n        table -> {\n          Snapshot matchingSnap = null;\n          for (Snapshot snap : table.snapshots()) {\n            if (wapId.equals(WapUtil.stagedWapId(snap))) {\n              if (matchingSnap != null) {\n                throw new ValidationException(\n                    \"Cannot apply non-unique WAP ID. Found multiple snapshots with WAP ID '%s'\",\n                    wapId);\n              } else {\n                matchingSnap = snap;\n              }\n            }\n          }\n\n          if (matchingSnap == null) {\n            throw new ValidationException(\"Cannot apply unknown WAP ID '%s'\", wapId);\n          }\n\n          long wapSnapshotId = matchingSnap.snapshotId();\n          table.manageSnapshots().cherrypick(wapSnapshotId).commit();\n          Snapshot currentSnapshot = table.currentSnapshot();\n          InternalRow outputRow = newInternalRow(wapSnapshotId, currentSnapshot.snapshotId());\n          return asScanIterator(OUTPUT_TYPE, outputRow);\n        });","sourceCodeStart":84,"sourceCodeEnd":120,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/procedures/PublishChangesProcedure.java#L84-L120","documentation":"PublishChangesProcedure cherry-picks a staged (WAP) snapshot identified by its write.wap.id property. ValidationException is thrown when more than one snapshot in the table carries the same WAP ID, because publishing would be ambiguous — the procedure refuses to guess which snapshot to publish.","triggerScenarios":"Calling CALL cat.system.publish_changes(table => ..., id => 'X') when two or more staged snapshots were written with the same write.wap.id value (e.g. reusing a WAP ID across retries or jobs without expiring/cherry-picking earlier ones).","commonSituations":"Hard-coding the same wap.id in a CI pipeline across repeated runs; retrying a failed Spark write with the same wap.id after the first attempt actually succeeded as a staged snapshot; long-lived staged snapshots never published or expired.","solutions":["Generate a unique wap.id per write job (e.g. UUID) instead of reusing values","Expire or delete the earlier duplicate staged snapshots, then retry publish_changes","Cherry-pick the intended snapshot manually via table.manageSnapshots().cherrypick(snapshotId) to disambiguate","List snapshots (SELECT * FROM table.snapshots) to find the duplicates before publishing"],"exampleFix":"// before\nspark.conf.set(\"write.wap.id\", \"fixed-id\"); // reused every run\n// after\nspark.conf.set(\"write.wap.id\", java.util.UUID.randomUUID().toString());","handlingStrategy":"validation","validationCode":"// detect duplicate WAP IDs before publishing\nval dups = spark.table(\"cat.db.t.snapshots\").where(\"properties[\\\"write.wap.id\\\"] = wapId\").count()\nif (dups > 1) fail(\"non-unique WAP ID\")","typeGuard":null,"tryCatchPattern":"try { spark.sql(call) } catch { case e: ValidationException if e.getMessage.contains(\"non-unique WAP ID\") => /* expire duplicates or cherry-pick manually */ }","preventionTips":["Generate a unique write.wap.id (UUID) per write job","Expire staged snapshots after publishing","Never hard-code WAP IDs in reusable pipelines"],"tags":["spark","procedures","wap","snapshot-conflict"],"backgroundTag":"invalid-state-transition","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}