apache/iceberg · error · UnsupportedOperationException

Cannot update statistics of a %s table

Error message

Cannot update statistics of a %s table

What it means

Statistics updates (write table statistics files) are mutation operations that BaseReadOnlyTable does not support. updateStatistics() always throws UnsupportedOperationException for a read-only table descriptor.

Source

Thrown at core/src/main/java/org/apache/iceberg/BaseReadOnlyTable.java:99

  public RowDelta newRowDelta() {
    throw new UnsupportedOperationException(
        "Cannot remove or replace rows in a " + descriptor + " table");
  }

  @Override
  public ReplacePartitions newReplacePartitions() {
    throw new UnsupportedOperationException(
        "Cannot replace partitions in a " + descriptor + " table");
  }

  @Override
  public DeleteFiles newDelete() {
    throw new UnsupportedOperationException("Cannot delete from a " + descriptor + " table");
  }

  @Override
  public UpdateStatistics updateStatistics() {
    throw new UnsupportedOperationException(
        "Cannot update statistics of a " + descriptor + " table");
  }

  @Override
  public UpdatePartitionStatistics updatePartitionStatistics() {
    throw new UnsupportedOperationException(
        "Cannot update partition statistics of a " + descriptor + " table");
  }

  @Override
  public ExpireSnapshots expireSnapshots() {
    throw new UnsupportedOperationException(
        "Cannot expire snapshots from a " + descriptor + " table");
  }

  @Override
  public ManageSnapshots manageSnapshots() {
    throw new UnsupportedOperationException(

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Use catalog.loadTable() to obtain a writable table handle before updating statistics.
  2. Skip statistics publication for read-only descriptors by checking the descriptor first.
  3. Publish statistics through a transaction on the real table.

Example fix

// before
readOnlyTable.updateStatistics().setStatisticsCommit(snapshotId, statsFile).commit();
// after
Table table = catalog.loadTable(tableIdentifier);
table.updateStatistics().setStatisticsCommit(snapshotId, statsFile).commit();
Defensive patterns

Strategy: validation

Validate before calling

if (!(table instanceof BaseReadOnlyTable)) {
  table.updateStatistics().setStatisticsCommit(snapshotId, statsFile).commit();
}

Type guard

boolean supportsStatsUpdate(Table t) { return !(t instanceof BaseReadOnlyTable); }

Try / catch

try { table.updateStatistics()...commit(); } catch (UnsupportedOperationException e) { LOG.warn("Skipping stats update on read-only table"); }

Prevention

When it happens

Trigger: Calling table.updateStatistics() on a read-only table instance, e.g. when a statistics-publishing job is handed a snapshot wrapper instead of the live table.

Common situations: Compute engines publishing statistics files after analysis jobs; automation that assumes every Table handle is writable.

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/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/b9766907e336e82d. Report an issue: GitHub.