apache/iceberg · error · UncheckedIOException

Failed to close position delete source

Error message

Failed to close position delete source

What it means

Deletes.toPositionIndexes consumes a CloseableIterable of position deletes, grouping positions into BitmapPositionDeleteIndex per data file, and closes the iterable at the end of a try block. If close() throws an IOException the library wraps it in UncheckedIOException. The indexes are built but the underlying delete-file streams failed to close cleanly.

Source

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

   */
  public static <T extends StructLike> CharSequenceMap<PositionDeleteIndex> toPositionIndexes(
      CloseableIterable<T> posDeletes, DeleteFile file) {
    CharSequenceMap<PositionDeleteIndex> indexes = CharSequenceMap.create();

    try (CloseableIterable<T> deletes = posDeletes) {
      String lastFilePath = null;
      PositionDeleteIndex index = null;
      for (T delete : deletes) {
        CharSequence filePath = (CharSequence) FILENAME_ACCESSOR.get(delete);
        long position = (long) POSITION_ACCESSOR.get(delete);
        if (lastFilePath == null || !lastFilePath.contentEquals(filePath)) {
          lastFilePath = filePath.toString();
          index = indexes.computeIfAbsent(filePath, key -> new BitmapPositionDeleteIndex(file));
        }
        index.delete(position);
      }
    } catch (IOException e) {
      throw new UncheckedIOException("Failed to close position delete source", e);
    }

    return indexes;
  }

  public static <T extends StructLike> PositionDeleteIndex toPositionIndex(
      CharSequence dataLocation, CloseableIterable<T> posDeletes, DeleteFile file) {
    CloseableIterable<Long> positions = extractPositions(dataLocation, posDeletes);
    List<DeleteFile> files = ImmutableList.of(file);
    return toPositionIndex(positions, files);
  }

  private static <T extends StructLike> CloseableIterable<Long> extractPositions(
      CharSequence dataLocation, CloseableIterable<T> rows) {
    DataFileFilter<T> filter = new DataFileFilter<>(dataLocation);
    CloseableIterable<T> filteredRows = filter.filter(rows);
    return CloseableIterable.transform(filteredRows, row -> (Long) POSITION_ACCESSOR.get(row));
  }

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Read the wrapped cause to identify the storage-level failure and fix FileIO config/permissions
  2. Ensure delete files are not being expired or rewritten concurrently while being read
  3. Retry the operation; transient object-storage errors often resolve
  4. Check network/filesystem stability between compute and storage layers
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-verify delete files are readable and not being expired
if (table.io().newInputFile(deleteFile.location()).exists()) { ... }

Try / catch

try {
  CharSequenceMap<PositionDeleteIndex> idx = Deletes.toPositionIndexes(posDeletes);
} catch (UncheckedIOException e) {
  throw new RetryableException("Delete source close failed: " + e.getCause().getMessage(), e.getCause());
}

Prevention

When it happens

Trigger: Calling Deletes.toPositionIndexes(CloseableIterable<PositionDelete>, ...) where the delete-file reader's close() raises IOException - e.g. Avro reader over a delete file that failed to flush/truncate or whose storage endpoint is unreachable at close time.

Common situations: S3/HDFS transient failures when finishing delete-file reads; delete files removed concurrently by expiration; misconfigured FileIO credentials surfacing only at stream finalization.

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/26f5e24c460f2f23. Report an issue: GitHub.