apache/beam · error · UnsupportedOperationException

Unknown delete file content

Error message

Unknown delete file content: {}

What it means

DeleteReader's constructor partitions a list of Iceberg DeleteFiles into position deletes and equality deletes using a switch over delete.content(). If the content type is anything other than POSITION_DELETES or EQUALITY_DELETES (e.g. a new content type added in a newer Iceberg version), it throws UnsupportedOperationException. This is a defensive guard against delete file kinds the Beam Iceberg CDC reader cannot process.

Solutions

  1. Check delete.content() before passing DeleteFiles to the DeleteReader and filter to POSITION_DELETES/EQUALITY_DELETES only
  2. Align the Iceberg runtime version used by the Beam pipeline with the version that wrote the table
  3. Upgrade the Beam SDK to a version supporting the delete content type
  4. Log and skip unsupported delete files if the use case tolerates partial reads

Example fix

// before
List<DeleteFile> allDeletes = task.deletes();
DeleteReader<?> reader = buildReader(allDeletes, ...);
// after
List<DeleteFile> supported = allDeletes.stream()
    .filter(d -> d.content() == FileContent.POSITION_DELETES
              || d.content() == FileContent.EQUALITY_DELETES)
    .collect(Collectors.toList());
DeleteReader<?> reader = buildReader(supported, ...);
Defensive patterns

Strategy: validation

Validate before calling

if (deletes.stream().anyMatch(d -> d.content() != FileContent.POSITION_DELETES
    && d.content() != FileContent.EQUALITY_DELETES)) {
  throw new IllegalArgumentException("Delete list contains unsupported delete file content");
}

Type guard

static boolean isSupportedDelete(DeleteFile d) {
  return d.content() == FileContent.POSITION_DELETES
      || d.content() == FileContent.EQUALITY_DELETES;
}

Try / catch

try {
  DeleteReader<?> reader = buildReader(deletes, tableSchema, expectedSchema, needRowPos, preloaded);
} catch (UnsupportedOperationException e) {
  LOG.error("Unsupported delete file content in table reads: {}", e.getMessage());
  throw new IOException("Incompatible delete files; align Iceberg versions", e);
}

Prevention

When it happens

Trigger: Constructing a DeleteReader (directly or via the Iceberg CDC BeamIO reader) with a delete list containing a DeleteFile whose content() is not POSITION_DELETES or EQUALITY_DELETES — for example a DataFileContent.DELETE introduced by a newer Iceberg format/library version.

Common situations: Running a Beam pipeline built against an older Iceberg SDK but reading a table written by a newer Iceberg writer that emitted an unsupported delete file content type; mixed-version Iceberg runtime dependencies; corrupted/mislabeled delete file metadata.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of apache/beam@12126d8942 (2026-09-13). Data as JSON: /api/errors/92cfa44be254d6bc. Report an issue: GitHub.

Appendix: source

Thrown at sdks/java/io/iceberg/src/main/java/org/apache/beam/sdk/io/iceberg/cdc/DeleteReader.java:97

      boolean needRowPosCol,
      PreloadedDeletes preloadedDeletes) {
    this.filePath = filePath;
    this.preloadedDeletes = preloadedDeletes;

    ImmutableList.Builder<DeleteFile> posDeleteBuilder = ImmutableList.builder();
    ImmutableList.Builder<DeleteFile> eqDeleteBuilder = ImmutableList.builder();
    for (DeleteFile delete : deletes) {
      switch (delete.content()) {
        case POSITION_DELETES:
          LOG.debug("Adding position delete file {} to reader", delete.location());
          posDeleteBuilder.add(delete);
          break;
        case EQUALITY_DELETES:
          LOG.debug("Adding equality delete file {} to reader", delete.location());
          eqDeleteBuilder.add(delete);
          break;
        default:
          throw new UnsupportedOperationException(
              "Unknown delete file content: " + delete.content());
      }
    }

    this.posDeletes = posDeleteBuilder.build();
    this.eqDeletes = eqDeleteBuilder.build();
    this.requiredSchema =
        fileProjection(tableSchema, expectedSchema, posDeletes, eqDeletes, needRowPosCol);
    this.posAccessor = requiredSchema.accessorForField(MetadataColumns.ROW_POSITION.fieldId());
  }

  public Schema requiredSchema() {
    return requiredSchema;
  }

  protected abstract StructLike asStructLike(T record);

  protected abstract InputFile getInputFile(String location);

View on GitHub (pinned to 12126d8942)