apache/iceberg · error · UncheckedIOException

Failed to close equality delete source

Error message

Failed to close equality delete source

What it means

Deletes.toEqualitySet materializes an equality-delete iterable into a StructLikeSet inside a try-with-resources block. If closing the underlying delete source (typically an open delete file) throws an IOException, the library rethrows it as an UncheckedIOException so callers of the scan/apply path don't have to handle checked exceptions. The deletes were likely read; the failure happens during resource cleanup.

Source

Thrown at core/src/main/java/org/apache/iceberg/deletes/Deletes.java:126

            if (deleted) {
              counter.increment();
            }

            return !deleted;
          }
        };

    return remainingRowsFilter.filter(rows);
  }

  public static StructLikeSet toEqualitySet(
      CloseableIterable<StructLike> eqDeletes, Types.StructType eqType) {
    try (CloseableIterable<StructLike> deletes = eqDeletes) {
      StructLikeSet deleteSet = StructLikeSet.create(eqType);
      Iterables.addAll(deleteSet, deletes);
      return deleteSet;
    } catch (IOException e) {
      throw new UncheckedIOException("Failed to close equality delete source", e);
    }
  }

  public static <T extends StructLike> CharSequenceMap<PositionDeleteIndex> toPositionIndexes(
      CloseableIterable<T> posDeletes) {
    return toPositionIndexes(posDeletes, null /* unknown delete file */);
  }

  /**
   * Builds a map of position delete indexes by path.
   *
   * <p>This method builds a position delete index for each referenced data file and does not filter
   * deletes. This can be useful when the entire delete file content is needed (e.g. caching).
   *
   * @param posDeletes position deletes
   * @param file the source delete file for the deletes
   * @return the map of position delete indexes by path
   */

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Inspect the wrapped IOException cause for the real storage error (permissions, missing file, network) and fix the underlying FileIO issue
  2. Check for concurrent table maintenance (rewrite/delete of the delete file) during the read and enable retry of the scan
  3. Verify the FileIO implementation's close path and storage connectivity (S3/HDFS) health
  4. Retry the query/scan; if transient storage errors persist, check cluster/network stability

Example fix

// before
CloseableIterable<StructLike> eqDeletes = openDeleteFile();
StructLikeSet set = Deletes.toEqualitySet(eqDeletes, eqType); // UncheckedIOException on close
// after
try {
  StructLikeSet set = Deletes.toEqualitySet(eqDeletes, eqType);
} catch (UncheckedIOException e) {
  LOG.error("Equality delete source close failed", e.getCause());
  throw new RuntimeException("Retry scan: delete file unreadable", e.getCause());
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate storage access before scan
try (CloseableIterable<StructLike> it = openEqualityDeletes(deleteFile)) {
  // peek: iteration will fail early if unreadable
  it.iterator().hasNext();
}

Try / catch

try {
  StructLikeSet set = Deletes.toEqualitySet(eqDeletes, eqType);
} catch (UncheckedIOException e) {
  LOG.error("Equality delete close failed", e.getCause());
  throw new RetryableScanException(e.getCause());
}

Prevention

When it happens

Trigger: Calling Deletes.toEqualitySet with a CloseableIterable whose close() throws IOException - e.g. an Avro/Parquet delete-file reader whose underlying FileIO stream fails while closing after full iteration.

Common situations: Underlying file deleted or truncated by concurrent compaction while reading equality deletes; HDFS/S3 I/O errors during stream close; network interruption to object storage mid-read of delete files.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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