apache/iceberg · error · UnsupportedOperationException
Cannot modify ${getClass().getName()}
Error message
Cannot modify ${getClass().getName()} What it means
Same contract as error 763: the EmptyPositionDeleteIndex singleton's range delete(long posStart, long posEnd) throws UnsupportedOperationException because the instance is immutable and shared. It exists only to answer isDeleted() with false and iterate zero positions.
Source
Thrown at core/src/main/java/org/apache/iceberg/deletes/EmptyPositionDeleteIndex.java:38
class EmptyPositionDeleteIndex implements PositionDeleteIndex {
private static final EmptyPositionDeleteIndex INSTANCE = new EmptyPositionDeleteIndex();
private EmptyPositionDeleteIndex() {}
static EmptyPositionDeleteIndex get() {
return INSTANCE;
}
@Override
public void delete(long position) {
throw new UnsupportedOperationException("Cannot modify " + getClass().getName());
}
@Override
public void delete(long posStart, long posEnd) {
throw new UnsupportedOperationException("Cannot modify " + getClass().getName());
}
@Override
public boolean isDeleted(long position) {
return false;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public String toString() {
return "PositionDeleteIndex{}";
}
}
View on GitHub (pinned to 86d9c8fc54)
Solutions
- Instantiate a concrete mutable index (BitmapPositionDeleteIndex) instead of using the singleton when writes are expected
- Guard batch-delete code paths with a check for EmptyPositionDeleteIndex before calling delete
- Use the index's merge() path with a mutable receiver if combining indexes
- Fix initialization logic so write-capable code never receives the empty singleton
Example fix
// before
EmptyPositionDeleteIndex.get().delete(0, 100); // UnsupportedOperationException
// after
PositionDeleteIndex idx = index instanceof EmptyPositionDeleteIndex
? new BitmapPositionDeleteIndex()
: index;
idx.delete(0, 100); Defensive patterns
Strategy: type-guard
Validate before calling
if (idx instanceof EmptyPositionDeleteIndex && needWrites) {
idx = new BitmapPositionDeleteIndex();
} Type guard
boolean supportsRangeDelete(PositionDeleteIndex idx) {
return !(idx instanceof EmptyPositionDeleteIndex);
} Try / catch
try {
idx.delete(start, end);
} catch (UnsupportedOperationException e) {
// fallback to mutable index
idx = new BitmapPositionDeleteIndex();
idx.delete(start, end);
} Prevention
- Never use the empty singleton as a write target
- Range-delete code should construct a fresh index if handed the singleton
- Unit-test write paths with a no-deletes table to catch singleton misuse
- Document that EmptyPositionDeleteIndex is immutable
When it happens
Trigger: Invoking delete(posStart, posEnd) on EmptyPositionDeleteIndex.get(), typically from a writer or merge path that received the singleton as its position-delete index for a file with no deletes.
Common situations: Custom merge-on-read code or DV handling that falls back to the empty index then attempts batch range deletes; tests exercising write paths against the singleton.
Understand the failure class
Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.
Related errors
- Cannot modify
- Cannot set value
- ${getClass().getName()} does not implement length
- ${getClass().getName()} does not support merge
- Not implemented: set
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/cae5447ae84bd42f.
Report an issue: GitHub.