apache/iceberg · error · ValidationException
Cannot delete data files %s that are referenced by new delet
Error message
Cannot delete data files %s that are referenced by new delete files
What it means
RowDelta operations both append delete (position/deletion-vector) files and can delete data files; this validation rejects commits where a data file being deleted is referenced by a delete file in the same operation. Deleting the data file would orphan the new delete file's positions, corrupting row-level deletion semantics.
Source
Thrown at core/src/main/java/org/apache/iceberg/BaseRowDelta.java:189
validateAddedDVs(base, startingSnapshotId, conflictDetectionFilter, parent);
}
}
/**
* Validates that the data files removed in this commit do not overlap with data files with delete
* files added
*/
@SuppressWarnings("CollectionUndefinedEquality")
private void validateNoConflictingFileAndPositionDeletes() {
List<CharSequence> deletedFileWithNewDVs =
removedDataFiles.stream()
.map(DataFile::path)
.filter(referencedDataFiles::contains)
.collect(Collectors.toList());
if (!deletedFileWithNewDVs.isEmpty()) {
throw new ValidationException(
"Cannot delete data files %s that are referenced by new delete files",
deletedFileWithNewDVs);
}
}
}
View on GitHub (pinned to 86d9c8fc54)
Solutions
- Split the work: commit the RowDelta that adds delete files first, then delete the data files in a separate, later operation (e.g. via deleteFiles/rewriteDataFiles)
- Refresh the table and re-validate that targeted data file paths are not in newly added delete files before deleting
- Use rewriteDataFiles instead of manual RowDelta so compaction and deletes are coordinated
- Coordinate merge-on-read writers and compaction jobs to avoid racing on the same files
Example fix
// before: same RowDelta does both Transaction tx = table.newTransaction(); table.newRowDelta().addDeletes(deleteFile).deleteDataFile(dataFile).commit(); // after: sequence operations table.newRowDelta().addDeletes(deleteFile).commit(); // snapshot A table.refresh(); table.newDelete().deleteFile(dataFile).commit(); // snapshot B
Defensive patterns
Strategy: validation
Validate before calling
Set<CharSequence> referenced = newDeleteFiles.stream().flatMap(df -> df.referencedDataFiles().stream()).collect(Collectors.toSet());
boolean conflict = dataFilesToDelete.stream().anyMatch(f -> referenced.contains(f.path()));
if (conflict) throw new IllegalStateException("data files referenced by new delete files"); Try / catch
try { rowDelta.commit(); } catch (ValidationException e) { table.refresh(); reapplyAsSeparateCommits(); } Prevention
- Never combine addDeletes with deleteDataFile for the same data files in one RowDelta
- Sequence DV publishing before compaction/file deletion
- Use high-level rewriteDataFiles APIs instead of manual RowDelta for maintenance
When it happens
Trigger: Calling newRowDelta() that both (a) addsDeleteFile / DV writers referencing data files and (b) deletes data files with deleteRows/deleteDataFiles for those same paths in one commit.
Common situations: Compaction jobs that remove rewritten data files while a concurrent merge-on-read writer publishes delete files targeting them; misconfigured maintenance pipelines issuing both changes in one RowDelta instead of separate snapshots.
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
- %s does not implement removeRows
- Unsupported delete file type:
- Unexpected delete file content:
- %s does not implement deleteFile
- %s does not implement addFile
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/920c5ab0c88239e5.
Report an issue: GitHub.