apache/iceberg · error · TableNotPartitionedException

TableNotPartitionedException

Error message

TableNotPartitionedException

What it means

listPartitions throws TableNotPartitionedException when the underlying Iceberg table is unpartitioned. Iceberg has no partition specs to enumerate for such tables, so Flink's catalog contract requires signaling that the table is not partitioned.

Source

Thrown at flink/v2.3/flink/src/main/java/org/apache/iceberg/flink/FlinkCatalog.java:818

  }

  @Override
  public void alterPartitionColumnStatistics(
      ObjectPath tablePath,
      CatalogPartitionSpec partitionSpec,
      CatalogColumnStatistics columnStatistics,
      boolean ignoreIfNotExists)
      throws CatalogException {
    throw new UnsupportedOperationException();
  }

  @Override
  public List<CatalogPartitionSpec> listPartitions(ObjectPath tablePath)
      throws TableNotExistException, TableNotPartitionedException, CatalogException {
    Table table = loadIcebergTable(tablePath);

    if (table.spec().isUnpartitioned()) {
      throw new TableNotPartitionedException(icebergCatalog.name(), tablePath);
    }

    Set<CatalogPartitionSpec> set = Sets.newHashSet();
    try (CloseableIterable<FileScanTask> tasks = table.newScan().planFiles()) {
      for (DataFile dataFile : CloseableIterable.transform(tasks, FileScanTask::file)) {
        Map<String, String> map = Maps.newHashMap();
        StructLike structLike = dataFile.partition();
        PartitionSpec spec = table.specs().get(dataFile.specId());
        for (int i = 0; i < structLike.size(); i++) {
          map.put(spec.fields().get(i).name(), String.valueOf(structLike.get(i, Object.class)));
        }
        set.add(new CatalogPartitionSpec(map));
      }
    } catch (IOException e) {
      throw new CatalogException(
          String.format("Failed to list partitions of table %s", tablePath), e);
    }

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Check table.spec().isUnpartitioned() before calling listPartitions and handle unpartitioned tables separately
  2. If partitions are expected, recreate/rewrite the table with a partition spec via Iceberg APIs
  3. Catch TableNotPartitionedException and fall back to whole-table scanning

Example fix

// before
List<CatalogPartitionSpec> parts = catalog.listPartitions(tablePath);

// after
if (!((FlinkCatalog) catalog).loadIcebergTable(tablePath).spec().isUnpartitioned()) {
  List<CatalogPartitionSpec> parts = catalog.listPartitions(tablePath);
}
Defensive patterns

Strategy: validation

Validate before calling

Table t = ((FlinkCatalog) catalog).loadIcebergTable(tablePath);
if (t.spec().isUnpartitioned()) { /* handle unpartitioned case */ } else { catalog.listPartitions(tablePath); }

Try / catch

try { catalog.listPartitions(tablePath); } catch (TableNotPartitionedException e) { /* scan whole table instead */ }

Prevention

When it happens

Trigger: Calling catalog.listPartitions(tablePath) (or Flink SQL operations that enumerate partitions) on an Iceberg table created with PartitionSpec.unpartitioned().

Common situations: Pointing Flink SQL at a plain Iceberg table and running partition-aware queries; generic tooling that lists partitions for any table; accidental use of an unpartitioned table where partitioned data was expected.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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