{"record":{"id":"cfe7a0038311f403","repo":"apache/iceberg","slug":"staging-snapshot-s-on-branch-s-contains-a-v2-p","errorCode":null,"errorMessage":"Staging snapshot %s on branch '%s' contains a V2 positional delete file (%s); equality delete conversion expects a V3 staging branch written by Flink, which produces only deletion vectors for deletes.","messagePattern":"Staging snapshot (.+?) on branch '(.+?)' contains a V2 positional delete file \\((.+?)\\); equality delete conversion expects a V3 staging branch written by Flink, which produces only deletion vectors for deletes\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/maintenance/operator/EqualityConvertPlanner.java","lineNumber":584,"sourceCode":"    for (DeleteFile deleteFile : changes.addedDeleteFiles()) {\n      if (deleteFile.content() == FileContent.EQUALITY_DELETES) {\n        Set<Integer> deleteFieldIds = Sets.newHashSet(deleteFile.equalityFieldIds());\n        Preconditions.checkState(\n            deleteFieldIds.equals(eqFieldIds),\n            \"Staging snapshot %s on branch '%s' contains an equality delete file %s with \"\n                + \"equalityFieldIds=%s, which does not match the configured eqFieldIds=%s. \"\n                + \"The writer must use the same equality field IDs as the converter.\",\n            stagingSnapshot.snapshotId(),\n            stagingBranch,\n            deleteFile.location(),\n            deleteFieldIds,\n            eqFieldIds);\n        validateDeleteSpecPartitionColumns(stagingSnapshot, deleteFile);\n        eqDeleteFiles.add(deleteFile);\n      } else if (ContentFileUtil.isDV(deleteFile)) {\n        stagingDVFiles.add(deleteFile);\n      } else {\n        throw new IllegalStateException(\n            String.format(\n                \"Staging snapshot %s on branch '%s' contains a V2 positional delete file (%s); \"\n                    + \"equality delete conversion expects a V3 staging branch written by Flink, \"\n                    + \"which produces only deletion vectors for deletes.\",\n                stagingSnapshot.snapshotId(), stagingBranch, deleteFile.location()));\n      }\n    }\n\n    return new StagingInputs(newDataFiles, stagingDVFiles, eqDeleteFiles);\n  }\n\n  private void validateDeleteSpecPartitionColumns(Snapshot stagingSnapshot, DeleteFile deleteFile) {\n    PartitionSpec spec = table.specs().get(deleteFile.specId());\n    for (PartitionField field : spec.fields()) {\n      Preconditions.checkState(\n          eqFieldIds.contains(field.sourceId()),\n          \"Staging snapshot %s on branch '%s' contains an equality delete file %s under spec %s, \"\n              + \"which partitions by field '%s' (source id %s) that is not an equality field %s. \"","sourceCodeStart":566,"sourceCodeEnd":602,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/maintenance/operator/EqualityConvertPlanner.java#L566-L602","documentation":"The planner found a V2 positional delete file among the staging branch's delete files. Equality delete conversion expects a V3 staging branch where Flink writes only deletion vectors (DVs); positional deletes indicate the table or branch is not in the expected format version or was written by a non-DV producer, so the planner fails fast.","triggerScenarios":"EqualityConvertPlanner.retrieveStagingFiles() iterates delete files of the staging snapshot and encounters a delete file that is neither an equality delete nor a DV (i.e. a position delete file).","commonSituations":"The table's format version is still 2 (or the branch was written by a V2 writer) so deletes are stored as positional delete files; an engine other than Flink (Spark, Trino) wrote positional deletes onto the staging branch; format-version upgrade not applied before running conversion.","solutions":["Set the table format version to >=3 (recreate or migrate the table) so Flink writes DVs instead of positional deletes.","Ensure all writers on the staging branch are Flink V3 DV writers; do not run Spark/Trino position-delete writers against the staging branch.","Identify which commit wrote the positional delete file (inspect snapshot history) and remove/rebase that commit from the staging branch.","Re-run the conversion after the staging branch contains only DVs and equality deletes."],"exampleFix":"// before\nCREATE TABLE t (...) TBLPROPERTIES ('format-version'='2');\n// after\nCREATE TABLE t (...) TBLPROPERTIES ('format-version'='3');","handlingStrategy":"validation","validationCode":"// pre-flight: table must be V3 and staging deletes must be DVs only\nif (table.operations().current().formatVersion() < 3)\n  throw new IllegalArgumentException(\"format-version >= 3 required\");\ntable.snapshot(branch).dataManifests(table.io()).forEach(m ->\n  ManifestFiles.readDeleteManifest(m, table.io(), null).deleteFiles()\n    .forEach(f -> checkState(ContentFileUtil.isDV(f) || f.content() == FileContent.EQUALITY_DELETES)));\n","typeGuard":"boolean allDeletesAreDvOrEq(Snapshot s, Table table) {\n  return s.deleteFiles(table.io()).stream()\n      .allMatch(f -> ContentFileUtil.isDV(f) || f.content() == FileContent.EQUALITY_DELETES);\n}","tryCatchPattern":"try {\n  planner.inputs();\n} catch (IllegalStateException e) {\n  if (e.getMessage().contains(\"positional delete file\")) {\n    migrateTableToV3(); // then rerun\n  } else throw e;\n}","preventionTips":["Set format-version=3 before enabling equality-delete conversion.","Only allow Flink V3 DV writers on the staging branch; block Spark/Trino writers there.","Verify writer engines' delete modes in pipeline configs (no position-delete mode).","Pre-flight scan staging manifests for non-DV delete files."],"tags":["flink","format-version","positional-delete"],"backgroundTag":"incompatible-source-type","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"}