{"record":{"id":"79356ea367ac90eb","repo":"apache/iceberg","slug":"update-must-be-represented-as-delete-and-insert","errorCode":null,"errorMessage":"Update must be represented as delete and insert","messagePattern":"Update must be represented as delete and insert","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/source/SparkPositionDeltaWrite.java","lineNumber":744,"sourceCode":"  }\n\n  private static class UnpartitionedDeltaWriter extends DeleteAndDataDeltaWriter {\n    private final PartitionSpec dataSpec;\n\n    UnpartitionedDeltaWriter(\n        Table table,\n        Map<String, DeleteFileSet> rewritableDeletes,\n        SparkFileWriterFactory writerFactory,\n        OutputFileFactory dataFileFactory,\n        OutputFileFactory deleteFileFactory,\n        Context context) {\n      super(table, rewritableDeletes, writerFactory, dataFileFactory, deleteFileFactory, context);\n      this.dataSpec = table.spec();\n    }\n\n    @Override\n    public void update(InternalRow meta, InternalRow id, InternalRow row) throws IOException {\n      throw new UnsupportedOperationException(\"Update must be represented as delete and insert\");\n    }\n\n    @Override\n    public void insert(InternalRow row) throws IOException {\n      delegate.insert(row, dataSpec, null);\n    }\n  }\n\n  private static class PartitionedDeltaWriter extends DeleteAndDataDeltaWriter {\n    private final PartitionSpec dataSpec;\n    private final PartitionKey dataPartitionKey;\n    private final InternalRowWrapper internalRowDataWrapper;\n\n    PartitionedDeltaWriter(\n        Table table,\n        Map<String, DeleteFileSet> rewritableDeletes,\n        SparkFileWriterFactory writerFactory,\n        OutputFileFactory dataFileFactory,","sourceCodeStart":726,"sourceCodeEnd":762,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/source/SparkPositionDeltaWrite.java#L726-L762","documentation":"Even in the file-scoped (rewritable deletes) delta writer, updates cannot be applied in place; Iceberg's Morpheus-style position delta protocol represents an update strictly as a delete plus an insert. The writer throws UnsupportedOperationException to enforce this contract.","triggerScenarios":"A MERGE/UPDATE operation emitting update rows (meta, id, row) to V2FileWriter/PositionDeltaWrite path where update ops reach this writer instead of separate delete+insert ops.","commonSituations":"Running UPDATE or MERGE WHEN MATCHED UPDATE on a v2 table using merge-on-read with the position delta writer, without the engine lowering updates into delete+insert pairs.","solutions":["Configure the write so updates are decomposed into delete+insert (this is the required representation).","Set the table property write.update.mode=copy-on-write (or merge mode) so the engine rewrites whole files for updates.","Check the Spark/Iceberg version pairing: older integrations did not lower updates for this writer; upgrade if updates must flow through."],"exampleFix":"// before\nMERGE INTO t ... WHEN MATCHED THEN UPDATE SET ... // direct update op\n// after\nALTER TABLE t SET TBLPROPERTIES ('write.update.mode'='copy-on-write')","handlingStrategy":"validation","validationCode":"// verify update lowering before writing\nString updateMode = table.properties().getOrDefault(\"write.update.mode\", \"merge-on-read\");\nif (!\"copy-on-write\".equals(updateMode)) { planUpdatesAsDeleteAndInsert(); }","typeGuard":null,"tryCatchPattern":"try { writer.update(meta, id, row); } catch (UnsupportedOperationException e) { writer.delete(meta, id); writer.insert(row); }","preventionTips":["Always decompose updates into delete+insert for merge-on-read v2 tables","Set write.update.mode=copy-on-write if direct update semantics are needed","Keep Spark and Iceberg versions aligned on delta write capabilities"],"tags":["spark","delta-write","merge-on-read","unsupported-operation"],"backgroundTag":"operation-not-supported","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"}