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

  1. Get a writable handle to the actual data table via catalog.loadTable(identifier) instead of using the metadata-table reference.
  2. Check whether the object is a read-only/metadata table before attempting schema changes.
  3. 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

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


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/690cd0069ffe7ace. Report an issue: GitHub.