apache/iceberg · error · UnsupportedOperationException

${getClass().getName()} does not support forEach

Error message

${getClass().getName()} does not support forEach

What it means

PositionDeleteIndex is an interface with default methods that throw UnsupportedOperationException for operations an implementation does not support. forEach() throws when the index is not empty and the concrete implementation (e.g. a stub or read-only index) has not overridden the default. It signals the index type cannot traverse its positions in order.

Source

Thrown at core/src/main/java/org/apache/iceberg/deletes/PositionDeleteIndex.java:78

   */
  boolean isDeleted(long position);

  /** Returns true if this collection contains no element. */
  boolean isEmpty();

  /** Returns true if this collection contains elements. */
  default boolean isNotEmpty() {
    return !isEmpty();
  }

  /**
   * Traverses all positions in the index in ascending order, applying the provided consumer.
   *
   * @param consumer a consumer for the positions
   */
  default void forEach(LongConsumer consumer) {
    if (isNotEmpty()) {
      throw new UnsupportedOperationException(getClass().getName() + " does not support forEach");
    }
  }

  /**
   * Returns delete files that this index was created from or an empty collection if unknown.
   *
   * @return delete files that this index was created from
   */
  default Collection<DeleteFile> deleteFiles() {
    return ImmutableList.of();
  }

  /** Returns the cardinality of this index. */
  default long cardinality() {
    throw new UnsupportedOperationException(getClass().getName() + " does not support cardinality");
  }

  /**

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Implement forEach(LongConsumer) in your PositionDeleteIndex implementation to traverse positions in ascending order
  2. Use a standard implementation such as BitmapPositionDeleteIndex instead of a custom stub
  3. Guard call sites: check the concrete type before calling forEach, or materialize via another supported access path

Example fix

// before
class StubIndex implements PositionDeleteIndex { /* no forEach */ }
// after
class StubIndex implements PositionDeleteIndex {
  @Override public void forEach(LongConsumer consumer) { positions.forEach(consumer); }
}
Defensive patterns

Strategy: validation

Validate before calling

if (index instanceof BitmapPositionDeleteIndex) { index.forEach(pos -> ...); } else if (index.isNotEmpty()) { throw new IllegalStateException("Index cannot be traversed"); }

Type guard

boolean supportsForEach(PositionDeleteIndex idx) { return idx instanceof BitmapPositionDeleteIndex; }

Try / catch

try { index.forEach(consumer); } catch (UnsupportedOperationException e) { log.warn("Index {} cannot be traversed", e.getMessage()); }

Prevention

When it happens

Trigger: Calling forEach on a PositionDeleteIndex implementation that does not override the default forEach, when isNotEmpty() is true. Raised from callers like merge, delete, writeDeletes, and test assertion helpers (assertEqual, collect) that traverse an index.

Common situations: Writing a custom PositionDeleteIndex (or using a minimal/test stub) that implements only some methods, then passing it to delete-file merge or writer code that iterates positions.

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/3f2b4ee5568471e1. Report an issue: GitHub.