apache/iceberg · error · UnsupportedOperationException

Cannot read files that require applying delete files

Error message

Cannot read files that require applying delete files

What it means

ArrowReader's VectorizedCombinedScanIterator eagerly checks every FileScanTask in the CombinedScanTask with TableScanUtil.hasDeletes. If any task requires applying delete files, the vectorized reader throws this UnsupportedOperationException because delete application is not supported on the Arrow read path.

Source

Thrown at arrow/src/main/java/org/apache/iceberg/arrow/vectorized/ArrowReader.java:241

     */
    VectorizedCombinedScanIterator(
        CloseableIterable<CombinedScanTask> tasks,
        Schema expectedSchema,
        String nameMapping,
        FileIO io,
        EncryptionManager encryptionManager,
        boolean caseSensitive,
        int batchSize,
        boolean reuseContainers) {
      List<FileScanTask> fileTasks =
          StreamSupport.stream(tasks.spliterator(), false)
              .map(CombinedScanTask::files)
              .flatMap(Collection::stream)
              .collect(Collectors.toList());
      this.fileItr = fileTasks.iterator();

      if (fileTasks.stream().anyMatch(TableScanUtil::hasDeletes)) {
        throw new UnsupportedOperationException(
            "Cannot read files that require applying delete files");
      }

      if (expectedSchema.columns().isEmpty()) {
        throw new UnsupportedOperationException(
            "Cannot read without at least one projected column");
      }

      Set<TypeID> unsupportedTypes =
          Sets.difference(
              expectedSchema.columns().stream()
                  .map(c -> c.type().typeId())
                  .collect(Collectors.toSet()),
              SUPPORTED_TYPES);
      if (!unsupportedTypes.isEmpty()) {
        throw new UnsupportedOperationException(
            "Cannot read unsupported column types: " + unsupportedTypes);
      }

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Rewrite/compact the table (e.g. rewrite_data_files / expire or apply deletes) so no delete files remain for the scanned snapshots.
  2. Fall back to the regular (row-based) record reader that supports delete application instead of the Arrow reader.
  3. Filter the scan to a snapshot before deletes were introduced if acceptable.
Defensive patterns

Strategy: validation

Validate before calling

if (task.files().stream().anyMatch(TableScanUtil::hasDeletes)) { /* use non-vectorized reader */ }

Type guard

static boolean isVectorizable(CombinedScanTask t) { return t.files().stream().noneMatch(TableScanUtil::hasDeletes); }

Try / catch

try { return new VectorizedCombinedScanIterator(...); } catch (UnsupportedOperationException e) { return nonVectorizedIterator(...); }

Prevention

When it happens

Trigger: Opening a VectorizedCombinedScanIterator (IcebergArrowReader) over a scan task containing equality- or position-delete files (e.g. a table with v2 delete files from MERGE/UPDATE operations).

Common situations: Reading a table that has had row-level deletes applied with vectorized Arrow reads enabled; Spark-less Arrow consumers scanning CDC-heavy tables.

Related errors


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