apache/iceberg · error · UnsupportedOperationException
${this.getClass().getName()} does not implement update
Error message
${this.getClass().getName()} does not implement update What it means
The position-delta writer variant that appends position deletes supports only write() of delete markers; Spark's RowDataWriter.update(metadata, id, row) contract for updating rows in place is not implemented and always throws UnsupportedOperationException. Updates to Iceberg tables must be expressed as a delete plus an insert.
Source
Thrown at spark/v4.2/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)
Solutions
- Ensure the Spark plan expresses updates as delete+insert (use Iceberg's standard UPDATE/MERGE support rather than calling update() on the writer)
- Use a writer implementation that supports updates (e.g. V2 copy-on-write path) if in-place update semantics are required
- Wrap the operation with Iceberg's procedures (CALL iceberg.system.merge/update) instead of custom writer code
Defensive patterns
Strategy: validation
Validate before calling
if (plan.requiresInPlaceUpdate()) {
throw new IllegalArgumentException("Use delete+insert or copy-on-write path");
} Prevention
- Never call update() on position-delete delta writers
- Use Iceberg's UPDATE/MERGE procedures which decompose updates correctly
- Route row mutations to writers that advertise support for them
When it happens
Trigger: Spark's MERGE/UPDATE execution invoking update() on the FanoutPositionDelete writer produced by SparkPositionDeltaWrite — i.e. a delta write plan that asks the position-delete delegate to update rows in place.
Common situations: Running UPDATE/MERGE statements whose plans attempt copy-on-write-style row updates through a position-delta (merge-on-read) writer; custom Spark sinks calling the wrong write method.
Related errors
- Update must be represented as delete and insert
- ${this.getClass().getName()} does not implement insert
- Cannot apply unknown table change: ${change}
- SparkCachedTableCatalog does not support altering tables
- SparkCachedTableCatalog does not support dropping tables
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/d07cf76de71edde5.
Report an issue: GitHub.