{"record":{"id":"573e1aa17fdb504f","repo":"apache/iceberg","slug":"does-not-implement-markrowdeleted","errorCode":null,"errorMessage":" does not implement markRowDeleted","messagePattern":" does not implement markRowDeleted","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"data/src/main/java/org/apache/iceberg/data/DeleteFilter.java","lineNumber":245,"sourceCode":"  public CloseableIterable<T> findEqualityDeleteRows(CloseableIterable<T> records) {\n    // Predicate to test whether a row has been deleted by equality deletions.\n    Predicate<T> deletedRows = applyEqDeletes().stream().reduce(Predicate::or).orElse(t -> false);\n\n    return CloseableIterable.filter(records, deletedRows);\n  }\n\n  private CloseableIterable<T> applyEqDeletes(CloseableIterable<T> records) {\n    if (eqDeletes.isEmpty()) {\n      return records;\n    }\n\n    Predicate<T> isEqDeleted = applyEqDeletes().stream().reduce(Predicate::or).orElse(t -> false);\n\n    return createDeleteIterable(records, isEqDeleted);\n  }\n\n  protected void markRowDeleted(T item) {\n    throw new UnsupportedOperationException(\n        this.getClass().getName() + \" does not implement markRowDeleted\");\n  }\n\n  public Predicate<T> eqDeletedRowFilter() {\n    if (eqDeleteRows == null) {\n      eqDeleteRows =\n          applyEqDeletes().stream().map(Predicate::negate).reduce(Predicate::and).orElse(t -> true);\n    }\n    return eqDeleteRows;\n  }\n\n  public PositionDeleteIndex deletedRowPositions() {\n    if (deleteRowPositions == null && !posDeletes.isEmpty()) {\n      this.deleteRowPositions = deleteLoader().loadPositionDeletes(posDeletes, filePath);\n    }\n\n    return deleteRowPositions;\n  }","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/data/src/main/java/org/apache/iceberg/data/DeleteFilter.java#L227-L263","documentation":"DeleteFilter.markRowDeleted is an extension hook that subclasses override to mark a record as deleted when applying equality deletes in-place. The base implementation throws UnsupportedOperationException, so calling it on a DeleteFilter subclass that did not override it (or a caller invoking the base method directly) fails.","triggerScenarios":"Calling markRowDeleted on a DeleteFilter instance whose concrete class does not override the method — e.g. a custom filter subclass that only implemented applyEqDeletes/eqDeletedRowFilter but uses a code path (like applyEqDeletes in mutation mode) that relies on markRowDeleted.","commonSituations":"Custom engine integrations extending DeleteFilter incompletely; refactors that changed which hook is used (filter-based vs mutation-based equality delete application); invoking protected hooks reflectively or from new code paths.","solutions":["Override markRowDeleted in your DeleteFilter subclass to implement the row-mutation semantics.","Use eqDeletedRowFilter()/applyEqDeletes() (predicate-based filtering) instead of the mutation path if you don't mutate rows in place.","Check which code path is calling markRowDeleted and switch to one your subclass supports.","Fix subclass inheritance so the concrete filter used at runtime actually implements the hook."],"exampleFix":"// before\npublic class MyFilter extends DeleteFilter<Record> { /* no markRowDeleted */ }\n\n// after\n@Override\nprotected void markRowDeleted(Record item) {\n  item.setField(\"_deleted\", true);\n}","handlingStrategy":"validation","validationCode":"if (filter.getClass().getMethod(\"markRowDeleted\")\n        .getDeclaringClass() == DeleteFilter.class) {\n  throw new IllegalStateException(\"Filter does not support in-place row deletion\");\n}","typeGuard":"boolean supportsMarkRowDeleted(DeleteFilter<?> f) {\n  try { f.getClass().getDeclaredMethod(\"markRowDeleted\", Object.class); return true; }\n  catch (NoSuchMethodException e) { return false; }\n}","tryCatchPattern":"try {\n  filter.markRowDeleted(item);\n} catch (UnsupportedOperationException e) {\n  // fall back to predicate-based filtering\n  Predicate<T> isDeleted = filter.eqDeletedRowFilter();\n}","preventionTips":["When subclassing DeleteFilter, override all hooks your code path relies on (markRowDeleted or eqDeletedRowFilter).","Add an abstract smoke test for custom filters that exercises both delete application paths.","Document which base methods are abstract-by-contract despite having default throwing implementations."],"tags":["unsupported-operation","delete-files","extension-point","subclass"],"backgroundTag":"method-not-implemented","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"}