apache/iceberg · error · UnsupportedOperationException

Cannot expire snapshots from a

Error message

Cannot expire snapshots from a %s table

What it means

Read-only table guard in BaseReadOnlyTable: expireSnapshots() mutates table state, which is not allowed on this read-only view of a table (e.g. a materialized/branch view described by 'descriptor'). Any call fails before touching metadata — the table handle, not the snapshots, is the limiting factor.

Solutions

  1. Call expireSnapshots() on the live table loaded via catalog.loadTable().
  2. Perform expiration inside a transaction on the writable table.
  3. In generic cleanup code, resolve the parent table handle before expiring.

Example fix

// before
readOnlyTable.expireSnapshots().expireOlderThan(ts).commit();
// after
Table table = catalog.loadTable(tableIdentifier);
table.expireSnapshots().expireOlderThan(ts).commit();
Defensive patterns

Strategy: validation

Validate before calling

if (!(table instanceof BaseReadOnlyTable)) {
  table.expireSnapshots().expireOlderThan(cutoff).commit();
}

Type guard

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

Try / catch

try { table.expireSnapshots()...commit(); } catch (UnsupportedOperationException e) { /* expire via the parent table instead */ }

Prevention

When it happens

Trigger: Calling table.expireSnapshots() on a snapshot wrapper or any BaseReadOnlyTable-backed descriptor.

Common situations: Retention/cleanup jobs iterating snapshots and attempting expiration directly on the snapshot object instead of the parent table.

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/5a82917df0056f10. Report an issue: GitHub.

Appendix: source

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

  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(
        "Cannot manage snapshots in a " + descriptor + " table");
  }

  @Override
  public Transaction newTransaction() {
    throw new UnsupportedOperationException(
        "Cannot create transactions for a " + descriptor + " table");
  }
}

View on GitHub (pinned to 86d9c8fc54)