apache/iceberg · error · UnsupportedOperationException
Cannot update the schema of a %s table
Error message
Cannot update the schema of a %s table
What it means
Iceberg throws this UnsupportedOperationException when updateSchema() is called on a read-only table. BaseReadOnlyTable is the base class for table views that only support reads (such as metadata tables); schema evolution requires a writable table. The descriptor in the message names what kind of read-only table it is.
Source
Thrown at core/src/main/java/org/apache/iceberg/BaseReadOnlyTable.java:31
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
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(View on GitHub (pinned to 86d9c8fc54)
Solutions
- Get a writable handle to the actual data table via catalog.loadTable(identifier) instead of using the metadata-table reference.
- Check whether the object is a read-only/metadata table before attempting schema changes.
- If you only need to read schema information, use table.schema() rather than updateSchema().
Example fix
// before
Table snapshots = table.snapshots();
snapshots.updateSchema().addColumn("new_col", Types.StringType.get()).commit();
// after
Table dataTable = catalog.loadTable(TableIdentifier.of("db", "tbl"));
dataTable.updateSchema().addColumn("new_col", Types.StringType.get()).commit(); Defensive patterns
Strategy: try-catch
Validate before calling
if (table.name().endsWith(".snapshots") || table.name().endsWith(".history") || table.name().endsWith(".files") || table.name().endsWith(".refs")) {
throw new IllegalArgumentException("Cannot evolve schema of metadata table: " + table.name());
} Type guard
boolean isMutable = !(table instanceof BaseReadOnlyTable); // package-private; practically: use only Table handles obtained from Catalog.loadTable
Try / catch
try {
table.updateSchema().addColumn("c", Types.StringType.get()).commit();
} catch (UnsupportedOperationException e) {
log.error("Table {} is read-only; load it from the catalog to mutate", table.name(), e);
} Prevention
- Only call write APIs on tables obtained from Catalog.loadTable, never on metadata-table handles.
- Name variables clearly (dataTable vs snapshotsView) to avoid mixing handles.
- Wrap generic maintenance code with a read-only check on the table kind.
When it happens
Trigger: Calling table.updateSchema() (directly or inside a transaction) on an instance whose class extends BaseReadOnlyTable, e.g. a metadata table like table.snapshots() or table.history().
Common situations: Developers iterating over table metadata objects assume every Table supports evolution and call updateSchema() on a metadata table reference, or pass such a reference into generic maintenance 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
- Cannot update the partition spec 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
- Cannot append to a %s table
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/690cd0069ffe7ace.
Report an issue: GitHub.