{"record":{"id":"2f8fc6d659a8aa6a","repo":"apache/iceberg","slug":"cannot-update-the-partition-spec-of-a-s-table","errorCode":null,"errorMessage":"Cannot update the partition spec of a %s table","messagePattern":"Cannot update the partition spec of a (.+?) table","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/apache/iceberg/BaseReadOnlyTable.java","lineNumber":37,"sourceCode":"package 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(\n        \"Cannot update the sort order of a \" + descriptor + \" table\");\n  }\n\n  @Override\n  public UpdateLocation updateLocation() {\n    throw new UnsupportedOperationException(","sourceCodeStart":19,"sourceCodeEnd":55,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/core/src/main/java/org/apache/iceberg/BaseReadOnlyTable.java#L19-L55","documentation":"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.","triggerScenarios":"Calling table.updateSpec() (or adding a partition field via the returned UpdatePartitionSpec) on an object extending BaseReadOnlyTable, such as a metadata table reference.","commonSituations":"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.","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."],"exampleFix":"// before\ntable.history().updateSpec().addField(\"day(ts)\").commit();\n\n// after\nTable dataTable = catalog.loadTable(TableIdentifier.of(\"db\", \"tbl\"));\ndataTable.updateSpec().addField(\"day(ts)\").commit();","handlingStrategy":"try-catch","validationCode":"if (!table.name().equals(baseTableIdentifier.name())) {\n  throw new IllegalArgumentException(\"Expected data table, got: \" + table.name());\n}","typeGuard":"boolean canEvolveSpec = !(table instanceof BaseReadOnlyTable); // prefer checking provenance: loaded from catalog","tryCatchPattern":"try {\n  table.updateSpec().addField(\"day(ts)\").commit();\n} catch (UnsupportedOperationException e) {\n  log.error(\"Cannot evolve partition spec of read-only table {}\", table.name(), e);\n}","preventionTips":["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."],"tags":["unsupported-operation","partition-spec","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"}