apache/iceberg · error · UnsupportedOperationException
this.getClass().getName() + " does not implement update"
Error message
this.getClass().getName() + " does not implement update"
What it means
The writer used for Spark's position-delta row-level operations does not implement the update() operation of the DeltaWriter interface. Iceberg represents updates in Copy-on-Write/Merge-on-Read mode as a paired delete plus insert, so a direct row update is never supported by this writer class. The exception reports the concrete class name to identify which writer variant was invoked.
Solutions
- Do not call update() on this writer; emit a delete followed by an insert for the changed row.
- Ensure the row-level operation mode (write.merge/rewrite or copy-on-write settings) routes updates through the standard delete+insert path.
- If this arises from Spark itself, report it with the Spark/Iceberg version; it indicates an unexpected planner path.
- Upgrade to a newer Iceberg release where the delta write path may handle this differently.
Example fix
// before writer.update(metadata, id, row); // after writer.delete(metadata, id); writer.insert(row);
Defensive patterns
Strategy: try-catch
Validate before calling
if (deltaWriter instanceof PositionDeleteWriter) { throw new IllegalStateException("use delete+insert instead of update"); } Try / catch
try { writer.update(meta, id, row); } catch (UnsupportedOperationException e) { writer.delete(meta, id); writer.insert(row); } Prevention
- Never call update() on SparkPositionDeltaWrite writers
- Always model updates as delete+insert
- Read the DeltaWriter interface docs before implementing custom handlers
When it happens
Trigger: Spark calls DeltaWriter.update(InternalRow metadata, InternalRow id, InternalRow row) on the position-delete-based writer inside SparkPositionDeltaWrite, e.g. when a row-level operation (UPDATE/MERGE/DELETE) attempts to route an updated row through update() instead of delete()+insert().
Common situations: Engine-internal code paths or custom Delta W handlers invoking update(); Spark planner changes routing updated rows directly; custom code reusing the writer API outside the normal delete+insert flow.
Related errors
- Altering a view is not supported by catalog:
- Altering a view is not supported by catalog
- Altering a view is not supported by catalog
- Altering a view is not supported by catalog
- Altering a view is not supported by catalog: catalogName
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/58ed1bdf8b826a31.
Report an issue: GitHub.
Appendix: source
Thrown at spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/source/SparkPositionDeltaWrite.java:647
@Override
public void delete(InternalRow metadata, InternalRow id) throws IOException {
int specId = metadata.getInt(specIdOrdinal);
PartitionSpec spec = specs.get(specId);
InternalRow partition = metadata.getStruct(partitionOrdinal, partitionRowWrapper.size());
StructProjection partitionProjection = partitionProjections.get(specId);
partitionProjection.wrap(partitionRowWrapper.wrap(partition));
String file = id.getString(fileOrdinal);
long position = id.getLong(positionOrdinal);
positionDelete.set(file, position);
delegate.write(positionDelete, spec, partitionProjection);
}
@Override
public void update(InternalRow metadata, InternalRow id, InternalRow row) {
throw new UnsupportedOperationException(
this.getClass().getName() + " does not implement update");
}
@Override
public void insert(InternalRow row) throws IOException {
throw new UnsupportedOperationException(
this.getClass().getName() + " does not implement insert");
}
@Override
public WriterCommitMessage commit() throws IOException {
close();
DeleteWriteResult result = delegate.result();
return new DeltaTaskCommit(result);
}
@OverrideView on GitHub (pinned to 86d9c8fc54)