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

  1. Instantiate a concrete mutable index (BitmapPositionDeleteIndex) instead of using the singleton when writes are expected
  2. Guard batch-delete code paths with a check for EmptyPositionDeleteIndex before calling delete
  3. Use the index's merge() path with a mutable receiver if combining indexes
  4. 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

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


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/cae5447ae84bd42f. Report an issue: GitHub.