apache/iceberg · error · UnsupportedOperationException
Update must be represented as delete and insert
Error message
Update must be represented as delete and insert
What it means
The copy-on-write (delta data) writer of SparkPositionDeltaWrite represents every UPDATE as a delete of the old row plus an insert of the new row, per Delta protocol semantics. It throws UnsupportedOperationException from update() because in-place updates are never sent to this writer. If you see it, the execution layer tried an operation the Iceberg Spark integration cannot express.
Source
Thrown at spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/source/SparkPositionDeltaWrite.java:797
SparkFileWriterFactory writerFactory,
OutputFileFactory dataFileFactory,
OutputFileFactory deleteFileFactory,
Function<InternalRow, InternalRow> rowLineageFromMetadata,
Context context) {
super(
table,
rewritableDeletes,
writerFactory,
dataFileFactory,
deleteFileFactory,
rowLineageFromMetadata,
context);
this.dataSpec = table.spec();
}
@Override
public void update(InternalRow meta, InternalRow id, InternalRow row) throws IOException {
throw new UnsupportedOperationException("Update must be represented as delete and insert");
}
@Override
public void insert(InternalRow row) throws IOException {
reinsert(null, row);
}
@Override
public void reinsert(InternalRow meta, InternalRow row) throws IOException {
delegate.insert(decorateWithRowLineage(meta, row), dataSpec, null);
}
}
private static class PartitionedDeltaWriter extends DeleteAndDataDeltaWriter {
private final PartitionSpec dataSpec;
private final PartitionKey dataPartitionKey;
private final InternalRowWrapper internalRowDataWrapper;
View on GitHub (pinned to 86d9c8fc54)
Solutions
- Use a Spark version compatible with the iceberg-spark 4.0 module so updates are split into delete+insert.
- Rewrite the command (MERGE/UPDATE) so it produces delete+insert, or run with the expected Spark Delta execution path.
- If working around, implement a custom RowLevelOperation whose writer translates update into delete+reinsert.
Defensive patterns
Strategy: try-catch
Validate before calling
Preconditions.checkArgument(command instanceof Delete || command instanceof Update || command instanceof Merge, "Only DELETE/UPDATE/MERGE are supported");
Try / catch
try { writer.update(meta, id, row); } catch (UnsupportedOperationException e) {
// rewrite operation as delete + insert
writer.delete(meta, id, oldRow); writer.insert(newRow);
} Prevention
- Keep Spark and iceberg-spark module versions aligned
- Express UPDATE/MERGE so the plan emits delete+insert pairs
- Avoid custom physical plans calling writer.update directly
When it happens
Trigger: Spark Delta batch execution invokes update(meta, id, row) on the copy-on-write SparkPositionDeltaWrite instead of decomposing the update into delete+insert (e.g. a Spark version or custom plan that skips the split).
Common situations: Version mismatch between Spark's row-level operation APIs and the pinned iceberg-spark v4.0 module; custom physical plans calling update directly.
Related errors
- this.getClass().getName() + does not implement update
- Table does not implement %s: %s (%s)
- this.getClass().getName() + does not implement insert
- Unsupported operation mode: + mode
- Unsupported command: + command
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/8f69ef720b3f8b49.
Report an issue: GitHub.