{"record":{"id":"690cd0069ffe7ace","repo":"apache/iceberg","slug":"cannot-update-the-schema-of-a-s-table","errorCode":null,"errorMessage":"Cannot update the schema of a %s table","messagePattern":"Cannot update the schema of a (.+?) table","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/BaseReadOnlyTable.java","lineNumber":31,"sourceCode":" * software distributed under the License is distributed on an\n * \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n * KIND, either express or implied.  See the License for the\n * specific language governing permissions and limitations\n * under the License.\n */\npackage org.apache.iceberg;\n\nabstract class BaseReadOnlyTable implements Table {\n\n  private final String descriptor;\n\n  BaseReadOnlyTable(String descriptor) {\n    this.descriptor = descriptor;\n  }\n\n  @Override\n  public UpdateSchema updateSchema() {\n    throw new UnsupportedOperationException(\n        \"Cannot update the schema of a \" + descriptor + \" table\");\n  }\n\n  @Override\n  public UpdatePartitionSpec updateSpec() {\n    throw new UnsupportedOperationException(\n        \"Cannot update the partition spec of a \" + descriptor + \" table\");\n  }\n\n  @Override\n  public UpdateProperties updateProperties() {\n    throw new UnsupportedOperationException(\n        \"Cannot update the properties of a \" + descriptor + \" table\");\n  }\n\n  @Override\n  public ReplaceSortOrder replaceSortOrder() {\n    throw new UnsupportedOperationException(","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/BaseReadOnlyTable.java#L13-L49","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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()."],"exampleFix":"// before\nTable snapshots = table.snapshots();\nsnapshots.updateSchema().addColumn(\"new_col\", Types.StringType.get()).commit();\n\n// after\nTable dataTable = catalog.loadTable(TableIdentifier.of(\"db\", \"tbl\"));\ndataTable.updateSchema().addColumn(\"new_col\", Types.StringType.get()).commit();","handlingStrategy":"try-catch","validationCode":"if (table.name().endsWith(\".snapshots\") || table.name().endsWith(\".history\") || table.name().endsWith(\".files\") || table.name().endsWith(\".refs\")) {\n  throw new IllegalArgumentException(\"Cannot evolve schema of metadata table: \" + table.name());\n}","typeGuard":"boolean isMutable = !(table instanceof BaseReadOnlyTable); // package-private; practically: use only Table handles obtained from Catalog.loadTable","tryCatchPattern":"try {\n  table.updateSchema().addColumn(\"c\", Types.StringType.get()).commit();\n} catch (UnsupportedOperationException e) {\n  log.error(\"Table {} is read-only; load it from the catalog to mutate\", table.name(), e);\n}","preventionTips":["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."],"tags":["unsupported-operation","schema-evolution","read-only-table"],"backgroundTag":"unsupported-operation","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}