{"record":{"id":"3f2b4ee5568471e1","repo":"apache/iceberg","slug":"getclass-getname-does-not-support-foreach","errorCode":null,"errorMessage":"${getClass().getName()} does not support forEach","messagePattern":"(.+?) does not support forEach","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/deletes/PositionDeleteIndex.java","lineNumber":78,"sourceCode":"   */\n  boolean isDeleted(long position);\n\n  /** Returns true if this collection contains no element. */\n  boolean isEmpty();\n\n  /** Returns true if this collection contains elements. */\n  default boolean isNotEmpty() {\n    return !isEmpty();\n  }\n\n  /**\n   * Traverses all positions in the index in ascending order, applying the provided consumer.\n   *\n   * @param consumer a consumer for the positions\n   */\n  default void forEach(LongConsumer consumer) {\n    if (isNotEmpty()) {\n      throw new UnsupportedOperationException(getClass().getName() + \" does not support forEach\");\n    }\n  }\n\n  /**\n   * Returns delete files that this index was created from or an empty collection if unknown.\n   *\n   * @return delete files that this index was created from\n   */\n  default Collection<DeleteFile> deleteFiles() {\n    return ImmutableList.of();\n  }\n\n  /** Returns the cardinality of this index. */\n  default long cardinality() {\n    throw new UnsupportedOperationException(getClass().getName() + \" does not support cardinality\");\n  }\n\n  /**","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/deletes/PositionDeleteIndex.java#L60-L96","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Implement forEach(LongConsumer) in your PositionDeleteIndex implementation to traverse positions in ascending order","Use a standard implementation such as BitmapPositionDeleteIndex instead of a custom stub","Guard call sites: check the concrete type before calling forEach, or materialize via another supported access path"],"exampleFix":"// before\nclass StubIndex implements PositionDeleteIndex { /* no forEach */ }\n// after\nclass StubIndex implements PositionDeleteIndex {\n  @Override public void forEach(LongConsumer consumer) { positions.forEach(consumer); }\n}","handlingStrategy":"validation","validationCode":"if (index instanceof BitmapPositionDeleteIndex) { index.forEach(pos -> ...); } else if (index.isNotEmpty()) { throw new IllegalStateException(\"Index cannot be traversed\"); }","typeGuard":"boolean supportsForEach(PositionDeleteIndex idx) { return idx instanceof BitmapPositionDeleteIndex; }","tryCatchPattern":"try { index.forEach(consumer); } catch (UnsupportedOperationException e) { log.warn(\"Index {} cannot be traversed\", e.getMessage()); }","preventionTips":["Always use BitmapPositionDeleteIndex or another complete implementation for production paths","Override every interface default that throws when writing custom indexes","Add unit tests that exercise forEach/serialize/cardinality on any custom index implementation"],"tags":["java","deletes","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}