apache/iceberg · error · UnsupportedOperationException
Cannot create TableScan from table of type POSITION_DELETES
Error message
Cannot create TableScan from table of type POSITION_DELETES
What it means
PositionDeletesTable is a metadata table exposing position delete files. It supports only batch scans, so TableScan.newScan() is intentionally unimplemented and throws UnsupportedOperationException. Callers must use the batch scan API instead.
Source
Thrown at core/src/main/java/org/apache/iceberg/PositionDeletesTable.java:80
PositionDeletesTable(Table table) {
this(table, table.name() + ".position_deletes");
}
PositionDeletesTable(Table table, String name) {
super(table, name);
this.schema = calculateSchema();
this.defaultSpecId = table.spec().specId();
this.specs = transformSpecs(schema(), table.specs());
}
@Override
MetadataTableType metadataTableType() {
return MetadataTableType.POSITION_DELETES;
}
@Override
public TableScan newScan() {
throw new UnsupportedOperationException(
"Cannot create TableScan from table of type POSITION_DELETES");
}
@Override
public BatchScan newBatchScan() {
return new PositionDeletesBatchScan(table(), schema());
}
@Override
public Schema schema() {
return schema;
}
@Override
public PartitionSpec spec() {
return specs.get(defaultSpecId);
}
View on GitHub (pinned to 86d9c8fc54)
Solutions
- Use PositionDeletesTable.newBatchScan() / BatchScan instead of newScan()
- Check table type before scanning: if the table is a metadata table (MetadataTableType.POSITION_DELETES), route to the batch scan API
- Cast to BatchScan: ((PositionDeletesTable) table).newBatchScan() and configure with .where()/project() equivalents on BatchScan
Example fix
// before TableScan scan = table.newScan(); // after BatchScan scan = ((PositionDeletesTable) table).newBatchScan();
Defensive patterns
Strategy: validation
Validate before calling
if (table instanceof PositionDeletesTable || MetadataTableUtils.metadataTableType(table) == MetadataTableType.POSITION_DELETES) {
BatchScan scan = ((PositionDeletesTable) table).newBatchScan();
} else {
TableScan scan = table.newScan();
} Type guard
boolean supportsTableScan(Table t) { return !(t instanceof PositionDeletesTable); } Try / catch
try { table.newScan(); } catch (UnsupportedOperationException e) { /* fall back to newBatchScan() */ } Prevention
- Route metadata tables (position_deletes, files, manifests) through BatchScan APIs
- Centralize scan creation in one helper that special-cases metadata tables
- Read the metadata-table docs: only batch scan is supported for position deletes
When it happens
Trigger: Calling newScan() (or table.scan()/TableScan-based refinement APIs) on a PositionDeletesTable instance obtained via MetastoreTables/MetadataTableUtils, e.g. scanning the 'tbl.position_deletes' metadata table through the streaming TableScan interface.
Common situations: Generic scan helper code that calls table.newScan() for any table without special-casing metadata tables; frameworks that default to TableScan; users assuming all Iceberg tables support incremental scans.
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
- %s does not implement deleteFile
- %s does not implement addFile
- %s does not implement dataSequenceNumber
- %s does not implement removeRows
- this.getClass().getName() + " doesn't implement removedDelet
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/692de72ea69db3a6.
Report an issue: GitHub.