apache/iceberg · error · IllegalStateException
Staging snapshot %s on branch '%s' contains a V2 positional
Error message
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.
What it means
EqualityConvertPlanner.retrieveStagingFiles() expects the staging branch to be format-v3-style output of Flink writers producing only deletion vectors. If a staged delete file is a V2 positional delete file (neither an equality delete handled above nor a DV), it throws with the file location, since such files cannot be converted.
Source
Thrown at flink/v2.1/flink/src/main/java/org/apache/iceberg/flink/maintenance/operator/EqualityConvertPlanner.java:584
for (DeleteFile deleteFile : changes.addedDeleteFiles()) {
if (deleteFile.content() == FileContent.EQUALITY_DELETES) {
Set<Integer> deleteFieldIds = Sets.newHashSet(deleteFile.equalityFieldIds());
Preconditions.checkState(
deleteFieldIds.equals(eqFieldIds),
"Staging snapshot %s on branch '%s' contains an equality delete file %s with "
+ "equalityFieldIds=%s, which does not match the configured eqFieldIds=%s. "
+ "The writer must use the same equality field IDs as the converter.",
stagingSnapshot.snapshotId(),
stagingBranch,
deleteFile.location(),
deleteFieldIds,
eqFieldIds);
validateDeleteSpecPartitionColumns(stagingSnapshot, deleteFile);
eqDeleteFiles.add(deleteFile);
} else if (ContentFileUtil.isDV(deleteFile)) {
stagingDVFiles.add(deleteFile);
} else {
throw new IllegalStateException(
String.format(
"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.",
stagingSnapshot.snapshotId(), stagingBranch, deleteFile.location()));
}
}
return new StagingInputs(newDataFiles, stagingDVFiles, eqDeleteFiles);
}
private void validateDeleteSpecPartitionColumns(Snapshot stagingSnapshot, DeleteFile deleteFile) {
PartitionSpec spec = table.specs().get(deleteFile.specId());
for (PartitionField field : spec.fields()) {
Preconditions.checkState(
eqFieldIds.contains(field.sourceId()),
"Staging snapshot %s on branch '%s' contains an equality delete file %s under spec %s, "
+ "which partitions by field '%s' (source id %s) that is not an equality field %s. "View on GitHub (pinned to 86d9c8fc54)
Solutions
- Upgrade the table to format-version 3 (ALTER TABLE ... SET TBLPROPERTIES ('format-version'='3')) and ensure writers produce DVs
- Stop non-Flink or legacy writers from committing positional deletes to the staging branch
- Set the Flink writer to emit deletion vectors (table property write.deletion-vectors.enabled=true) and re-run the cycle
- Point conversion at a clean staging branch that only contains DV-producing commits
Example fix
// before
ALTER TABLE db.t SET TBLPROPERTIES ('format-version'='2')
// after
ALTER TABLE db.t SET TBLPROPERTIES ('format-version'='3', 'write.deletion-vectors.enabled'='true') Defensive patterns
Strategy: validation
Validate before calling
// preflight before conversion
if (table.properties().getOrDefault("format-version", "1").compareTo("3") < 0
|| !"true".equals(table.properties().get("write.deletion-vectors.enabled"))) {
throw new IllegalStateException("Staging branch must be V3 with DVs enabled");
} Prevention
- Upgrade tables to format-version 3 before enabling equality-delete conversion
- Enable write.deletion-vectors.enabled for all Flink writers
- Prevent Spark/legacy writers from committing to the staging branch
- Audit the staging branch's delete files (must be DVs only) before planning
When it happens
Trigger: The staging branch snapshot contains positional delete files — e.g. the table/branch is still format-version 2, or writers other than the Flink DV writer (Spark, older Flink writer) committed positional deletes to the staging branch.
Common situations: Table not upgraded to format-version 3 before running conversion; mixed-engine writes to the staging branch; fallback writer configuration emitting positional deletes instead of DVs; checkpoint restored data from a pre-upgrade snapshot.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Staging snapshot %s on branch '%s' contains a V2 positional
- V2 positional delete file %s attached to main data file %s;
- Staging snapshot %s on branch '%s' removes data files; equal
- Cannot write manifest for table version: %s
- Bitmap decoding has not been implemented
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/72ac59a5871c63b4.
Report an issue: GitHub.