{"record":{"id":"277a7d161fed000f","repo":"apache/iceberg","slug":"deleted-rows-scan-task-is-not-supported-yet-277a7d","errorCode":null,"errorMessage":"Deleted rows scan task is not supported yet","messagePattern":"Deleted rows scan task is not supported yet","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/source/ChangelogRowReader.java","lineNumber":108,"sourceCode":"    return cdcRows.iterator();\n  }\n\n  private static InternalRow changelogMetadata(ChangelogScanTask task) {\n    InternalRow metadataRow = new GenericInternalRow(3);\n\n    metadataRow.update(0, UTF8String.fromString(task.operation().name()));\n    metadataRow.update(1, task.changeOrdinal());\n    metadataRow.update(2, task.commitSnapshotId());\n\n    return metadataRow;\n  }\n\n  private CloseableIterable<InternalRow> openChangelogScanTask(ChangelogScanTask task) {\n    if (task instanceof AddedRowsScanTask) {\n      return openAddedRowsScanTask((AddedRowsScanTask) task);\n\n    } else if (task instanceof DeletedRowsScanTask) {\n      throw new UnsupportedOperationException(\"Deleted rows scan task is not supported yet\");\n\n    } else if (task instanceof DeletedDataFileScanTask) {\n      return openDeletedDataFileScanTask((DeletedDataFileScanTask) task);\n\n    } else {\n      throw new IllegalArgumentException(\n          \"Unsupported changelog scan task type: \" + task.getClass().getName());\n    }\n  }\n\n  CloseableIterable<InternalRow> openAddedRowsScanTask(AddedRowsScanTask task) {\n    String filePath = task.file().location();\n    SparkDeleteFilter deletes = new SparkDeleteFilter(filePath, task.deletes(), counter(), true);\n    return deletes.filter(rows(task, deletes.requiredSchema()));\n  }\n\n  private CloseableIterable<InternalRow> openDeletedDataFileScanTask(DeletedDataFileScanTask task) {\n    String filePath = task.file().location();","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/source/ChangelogRowReader.java#L90-L126","documentation":"ChangelogRowReader.openChangelogScanTask dispatches changelog scan tasks by type. DeletedRowsScanTask (equal-opportunity deletions of individual rows, e.g. from equality deletes in incremental changelog mode) is explicitly unsupported and throws UnsupportedOperationException. Deleted data files are supported, but per-row deletion records are not.","triggerScenarios":"Reading a changelog/incremental stream (spark.read-stream.format('iceberg').option('stream-scan-interval'...) or batch incremental scan) where a changelog task contains a DeletedRowsScanTask — produced when delete files (position/equality deletes) must be expanded into per-row 'D' records.","commonSituations":"Streaming changelog reads over tables with v2 equality-delete writes (e.g. Flink CDC writers with upsert mode); merge-on-read tables with delete files in the increment range.","solutions":["Avoid changelog mode over tables written with equality/position deletes in the increment range, or compact (rewrite data files) so deletes are applied and removed.","Use copy-on-write mode for the writing engine so deletes rewrite files (yielding DeletedDataFileScanTask, which is supported).","Run rewrite_delete_files / data compaction actions before reading the increment.","Track Iceberg releases — per-row deleted-records changelog support may land in newer versions."],"exampleFix":"// before\nspark.readStream.option('stream-from-timestamp', ts).table('tbl') // fails on eq-deletes\n// after\nspark.sql(\"CALL system.rewrite_data_files(table => 'db.tbl')\")\n// or write with copy-on-write so deletes produce whole-file records","handlingStrategy":"validation","validationCode":"// ensure increment range has no unapplied delete files before changelog read\nboolean hasDeletes = table.snapshots().stream()\n    .filter(s -> s.snapshotId() >= fromSnapshotId)\n    .flatMap(s -> s.deleteManifests(table.io()).stream())\n    .anyMatch(m -> m.hasDeletedFiles());\nif (hasDeletes) runRewriteDeleteFiles();","typeGuard":null,"tryCatchPattern":"try { readChangelog(); } catch (UnsupportedOperationException e) {\n  if (e.getMessage().contains(\"Deleted rows scan task\")) {\n    compactTable(); readChangelog();\n  } else throw e;\n}","preventionTips":["Prefer copy-on-write writers for tables read in changelog mode.","Schedule regular rewrite_delete_files / compaction maintenance.","Document that merge-on-read equality deletes are incompatible with changelog streaming."],"tags":["spark","changelog","delete-files","unsupported"],"backgroundTag":"unsupported-operation","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"}