{"record":{"id":"692de72ea69db3a6","repo":"apache/iceberg","slug":"cannot-create-tablescan-from-table-of-type-positio","errorCode":null,"errorMessage":"Cannot create TableScan from table of type POSITION_DELETES","messagePattern":"Cannot create TableScan from table of type POSITION_DELETES","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/PositionDeletesTable.java","lineNumber":80,"sourceCode":"  PositionDeletesTable(Table table) {\n    this(table, table.name() + \".position_deletes\");\n  }\n\n  PositionDeletesTable(Table table, String name) {\n    super(table, name);\n    this.schema = calculateSchema();\n    this.defaultSpecId = table.spec().specId();\n    this.specs = transformSpecs(schema(), table.specs());\n  }\n\n  @Override\n  MetadataTableType metadataTableType() {\n    return MetadataTableType.POSITION_DELETES;\n  }\n\n  @Override\n  public TableScan newScan() {\n    throw new UnsupportedOperationException(\n        \"Cannot create TableScan from table of type POSITION_DELETES\");\n  }\n\n  @Override\n  public BatchScan newBatchScan() {\n    return new PositionDeletesBatchScan(table(), schema());\n  }\n\n  @Override\n  public Schema schema() {\n    return schema;\n  }\n\n  @Override\n  public PartitionSpec spec() {\n    return specs.get(defaultSpecId);\n  }\n","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/PositionDeletesTable.java#L62-L98","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nTableScan scan = table.newScan();\n// after\nBatchScan scan = ((PositionDeletesTable) table).newBatchScan();","handlingStrategy":"validation","validationCode":"if (table instanceof PositionDeletesTable || MetadataTableUtils.metadataTableType(table) == MetadataTableType.POSITION_DELETES) {\n  BatchScan scan = ((PositionDeletesTable) table).newBatchScan();\n} else {\n  TableScan scan = table.newScan();\n}","typeGuard":"boolean supportsTableScan(Table t) { return !(t instanceof PositionDeletesTable); }","tryCatchPattern":"try { table.newScan(); } catch (UnsupportedOperationException e) { /* fall back to newBatchScan() */ }","preventionTips":["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"],"tags":["iceberg","metadata-table","unsupported-operation"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}