apache/iceberg · error · UnsupportedOperationException
Operation expireSnapshots is not supported after the table…
Error message
Operation expireSnapshots is not supported after the table is serialized
What it means
SerializableTable is a read-only, serializable snapshot of a table meant to be shipped to other nodes in a cluster (e.g. Spark executors). It deliberately rejects all mutation operations, including snapshot expiration, because writes on a deserialized copy cannot be committed back to the catalog. Calling expireSnapshots() on such a table throws this UnsupportedOperationException by design.
Solutions
- Reload the table from its catalog on the node where the mutation runs (catalog.loadTable(location)) and call expireSnapshots() there, e.g. in the driver.
- Use the convenience entry point HiveCatalog/SparkActions (e.g. Spark Actions expireSnapshots(table)) which handles reloading, instead of calling the Table API on a serialized copy.
- Guard mutation code with an instanceof SerializableTable check and route it to a fresh catalog-loaded Table.
Example fix
// before Table table = broadcastTable.value(); // SerializableTable on executor table.expireSnapshots().olderThan(ts).execute(); // after Table table = catalog.loadTable(tableLocation); // reload on the mutating node table.expireSnapshots().olderThan(ts).execute();
Defensive patterns
Strategy: type-guard
Validate before calling
if (table instanceof org.apache.iceberg.SerializableTable) {
table = catalog.loadTable(table.location()); // reload before expiring snapshots
} Type guard
boolean isMutable(Table t) {
return !(t instanceof org.apache.iceberg.SerializableTable);
} Try / catch
try {
table.expireSnapshots().olderThan(ts).execute();
} catch (UnsupportedOperationException e) {
// fall back to catalog-loaded table
catalog.loadTable(table.location()).expireSnapshots().olderThan(ts).execute();
} Prevention
- Only broadcast/serialize tables for read and scan work; keep all mutation on catalog-loaded tables.
- Relocate maintenance operations (expire, rollback, transactions) to the driver.
- Document helper methods to accept a Catalog plus location instead of a possibly-serialized Table.
When it happens
Trigger: Calling table.expireSnapshots() on a Table instance that is actually a SerializableTable — i.e. a table obtained after Java/Kryo serialization (typically inside a distributed task) or created via SerializableTable.copyOf(table).
Common situations: Running maintenance code (snapshot expiration, orphan file cleanup wrappers) inside a Spark/Flink executor where the table was broadcast or serialized with the task closure; holding a SerializableTable reference and assuming it behaves like a full BaseTable from a Catalog.
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
- Cannot serialize type: + typeId
- Operation manageSnapshots is not supported after the table…
- Operation newAppend is not supported after the table is…
- Operation newDelete is not supported after the table is…
- Operation newOverwrite is not supported after the table is…
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/f7ad734c38469d86.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/apache/iceberg/SerializableTable.java:422
@Override
public DeleteFiles newDelete() {
throw new UnsupportedOperationException(errorMsg("newDelete"));
}
@Override
public UpdateStatistics updateStatistics() {
throw new UnsupportedOperationException(errorMsg("updateStatistics"));
}
@Override
public UpdatePartitionStatistics updatePartitionStatistics() {
throw new UnsupportedOperationException(errorMsg("updatePartitionStatistics"));
}
@Override
public ExpireSnapshots expireSnapshots() {
throw new UnsupportedOperationException(errorMsg("expireSnapshots"));
}
@Override
public ManageSnapshots manageSnapshots() {
throw new UnsupportedOperationException(errorMsg("manageSnapshots"));
}
@Override
public Transaction newTransaction() {
throw new UnsupportedOperationException(errorMsg("newTransaction"));
}
@Override
public StaticTableOperations operations() {
return (StaticTableOperations) ((BaseTable) lazyTable()).operations();
}
private String errorMsg(String operation) {View on GitHub (pinned to 86d9c8fc54)