{"record":{"id":"5710ccbc41cb3340","repo":"apache/iceberg","slug":"cannot-apply-non-unique-wap-id-found-multiple-sna-5710cc","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":"exception","errorClass":"ValidationException","httpStatus":null,"severity":"error","filePath":"spark/v4.0/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.0/spark/src/main/java/org/apache/iceberg/spark/procedures/PublishChangesProcedure.java#L84-L120","documentation":"PublishChangesProcedure cherry-picks a staged (write-audit-publish) snapshot identified by its 'wap.id' snapshot summary property. Before publishing, it scans all snapshots for the given WAP ID; if two or more staged snapshots carry the same ID, it aborts, because it cannot determine which snapshot to publish. This guards against reusing a WAP ID across commits.","triggerScenarios":"Calling CALL iceberg.system.publish_changes(table => 'db.t', wap_id => 'some-id') when the table's history contains two staged snapshots both stamped with that same wap.id — typically because a writer reused a fixed wap.id value across multiple writes without publishing in between.","commonSituations":"A Spark job sets spark.wap.id to a static value in config instead of a per-run UUID; two concurrent/staged branches of the same job write with the same WAP ID; a failed publish followed by a re-run staged a second snapshot with the same ID.","solutions":["Ensure each staged write gets a unique WAP ID (per-run UUID), e.g. derive spark.wap.id per execution.","Expire or delete the stale staged snapshots so only one carries the ID, then retry publish.","Publish the specific snapshot explicitly instead: cherry-pick the desired snapshotId via manageSnapshots/rewrite the procedure call.","Audit the writer configuration generating wap.id for reuse."],"exampleFix":"// before (reused static id)\nspark.conf.set(\"spark.wap.id\", \"run-42\");\n// after (unique per run)\nspark.conf.set(\"spark.wap.id\", java.util.UUID.randomUUID().toString());","handlingStrategy":"validation","validationCode":"SELECT snapshot_id, summary['wap.id'] FROM db.t.snapshots WHERE summary['wap.id'] = 'my-id'; // expect exactly one row","typeGuard":null,"tryCatchPattern":"try { publishChanges(table, wapId); } catch (ValidationException e) { /* duplicate WAP ID: expire stale snapshot or use unique IDs */ }","preventionTips":["Generate a fresh UUID per write for spark.wap.id.","Never hardcode a static WAP ID across runs.","Expire stale staged snapshots before re-staging."],"tags":["spark","wap","snapshot-conflict"],"backgroundTag":"conflicting-config-options","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"}