apache/iceberg · error · UnsupportedOperationException
this.getClass().getName() + does not implement update
Error message
this.getClass().getName() + does not implement update
What it means
The copy-on-write position-delete writer for MERGE/Delta operations only supports writing whole-row deletes identified by file and position. It deliberately throws UnsupportedOperationException from update() because updating a row in place is not representable — Spark Delta semantics require updates to be decomposed into delete+insert before reaching this writer.
Source
Thrown at spark/v4.0/spark/src/main/java/org/apache/iceberg/spark/source/SparkPositionDeltaWrite.java:644
@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 updates are decomposed by Spark into delete+insert (use supported Spark/Iceberg version pairs for the v4.0 module).
- Do not call update() directly on this writer; use delete() followed by insert()/reinsert().
- If extending the writer, implement update() in your subclass instead of relying on the default.
- Check that the command is routed through SparkRowLevelOperationBuilder, which produces the correct operation for the mode.
Example fix
// before: direct update call on the writer writer.update(metadataRow, idRow, updatedRow); // after: express as delete + insert writer.delete(metadataRow, idRow); writer.insert(updatedRow);
Defensive patterns
Strategy: type-guard
Validate before calling
if (writer instanceof SparkPositionDeltaWrite && supportsUpdate(writer)) {
writer.update(meta, id, row);
} else {
writer.delete(meta, id, row);
writer.insert(row);
} Type guard
boolean supportsInPlaceUpdate(Object w) { return w instanceof SupportsUpdate; } Try / catch
try { writer.update(meta, id, row); } catch (UnsupportedOperationException e) {
writer.delete(meta, id, row); writer.insert(row);
} Prevention
- Never call update() directly on Iceberg row-level writers; always decompose into delete+insert
- Use supported Spark/Iceberg version pairs
- Route writes through SparkRowLevelOperationBuilder, not writer internals
When it happens
Trigger: A Spark DELETE/MERGE ... UPDATE execution path calls update(metadata, id, row) on the SparkPositionDeltaWrite copy-on-write writer — only possible if a newer Spark Delta API surface routes an update op directly to this writer instead of split delete+insert.
Common situations: Custom or forked Spark integrations invoking the row-level writer API directly; running against a Spark version whose Delta execution calls update() rather than delete(); misuse of the internal writer API in tests/tools.
Related errors
- Update must be represented as delete and insert
- Table does not implement %s: %s (%s)
- Update must be represented as delete and insert
- this.getClass().getName() + does not implement insert
- Unsupported operation mode: + mode
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/1bcb766a894c97c5.
Report an issue: GitHub.