apache/iceberg · error · UnsupportedOperationException
Cannot update the partition spec of a %s table
Error message
Cannot update the partition spec of a %s table
What it means
Iceberg throws this UnsupportedOperationException when updateSpec() is called on a read-only table. Partition spec evolution mutates table metadata, which BaseReadOnlyTable subclasses (e.g. metadata tables) do not permit. The descriptor identifies the read-only table kind.
Source
Thrown at core/src/main/java/org/apache/iceberg/BaseReadOnlyTable.java:37
package org.apache.iceberg;
abstract class BaseReadOnlyTable implements Table {
private final String descriptor;
BaseReadOnlyTable(String descriptor) {
this.descriptor = descriptor;
}
@Override
public UpdateSchema updateSchema() {
throw new UnsupportedOperationException(
"Cannot update the schema of a " + descriptor + " table");
}
@Override
public UpdatePartitionSpec updateSpec() {
throw new UnsupportedOperationException(
"Cannot update the partition spec of a " + descriptor + " table");
}
@Override
public UpdateProperties updateProperties() {
throw new UnsupportedOperationException(
"Cannot update the properties of a " + descriptor + " table");
}
@Override
public ReplaceSortOrder replaceSortOrder() {
throw new UnsupportedOperationException(
"Cannot update the sort order of a " + descriptor + " table");
}
@Override
public UpdateLocation updateLocation() {
throw new UnsupportedOperationException(View on GitHub (pinned to 86d9c8fc54)
Solutions
- Load the writable data table from the catalog (catalog.loadTable(...)) and call updateSpec() on it.
- Guard the call: skip spec evolution when the table is a metadata/read-only table.
- If only reading the current spec is needed, use table.spec() instead.
Example fix
// before
table.history().updateSpec().addField("day(ts)").commit();
// after
Table dataTable = catalog.loadTable(TableIdentifier.of("db", "tbl"));
dataTable.updateSpec().addField("day(ts)").commit(); Defensive patterns
Strategy: try-catch
Validate before calling
if (!table.name().equals(baseTableIdentifier.name())) {
throw new IllegalArgumentException("Expected data table, got: " + table.name());
} Type guard
boolean canEvolveSpec = !(table instanceof BaseReadOnlyTable); // prefer checking provenance: loaded from catalog
Try / catch
try {
table.updateSpec().addField("day(ts)").commit();
} catch (UnsupportedOperationException e) {
log.error("Cannot evolve partition spec of read-only table {}", table.name(), e);
} Prevention
- Load spec-evolution targets directly from the catalog.
- Skip metadata tables when enumerating tables for maintenance.
- Inspect table.spec() for reads instead of opening an update.
When it happens
Trigger: Calling table.updateSpec() (or adding a partition field via the returned UpdatePartitionSpec) on an object extending BaseReadOnlyTable, such as a metadata table reference.
Common situations: Maintenance jobs that evolve partitioning generically over any Table handle hit metadata tables, or a wrong variable holding a snapshots()/history() reference is passed to spec-evolution code.
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
- this.getClass().getName() + " doesn't implement addNonDefaul
- Cannot update the schema of a %s table
- Cannot update the properties of a %s table
- Cannot update the sort order of a %s table
- Cannot update the location of a %s table
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/2f8fc6d659a8aa6a.
Report an issue: GitHub.