apache/iceberg · error · UnsupportedOperationException

Cannot apply unknown table change:

Error message

Cannot apply unknown table change: 

What it means

The final else branch of applySchemaChanges rejects any TableChange type the util does not explicitly handle, preventing silent loss of schema updates. The thrown message includes the change's toString.

Solutions

  1. Upgrade iceberg-flink to a version handling the new change type
  2. Filter out unsupported TableChange instances before calling applySchemaChanges
  3. Apply the unhandled change through a different mechanism (raw Iceberg API, Spark)
  4. Log and ignore the change if it is semantically a no-op for Iceberg

Example fix

// before
FlinkAlterTableUtil.applySchemaChanges(table, changes);
// after
List<TableChange> supported = changes.stream().filter(c ->
    c instanceof TableChange.AddColumn || c instanceof TableChange.DropColumn
    || c instanceof TableChange.SetOption /* ... */).collect(Collectors.toList());
FlinkAlterTableUtil.applySchemaChanges(table, supported);
Defensive patterns

Strategy: validation

Validate before calling

Set<Class<?>> supported = Set.of(TableChange.AddColumn.class, TableChange.DropColumn.class,
    TableChange.ModifyColumn.class, TableChange.AddUniqueConstraint.class,
    TableChange.SetOption.class);
boolean allSupported = changes.stream().allMatch(c ->
    supported.stream().anyMatch(s -> s.isInstance(c)));

Try / catch

try {
  FlinkAlterTableUtil.applySchemaChanges(table, changes);
} catch (UnsupportedOperationException e) {
  // identify the offending change from the message and route it elsewhere
}

Prevention

When it happens

Trigger: Passing a TableChange subclass not covered by the if/else chain (e.g. a new Flink TableChange type added in a newer Flink version) into applySchemaChanges.

Common situations: Upgrading Flink and using newly introduced TableChange types with an older Iceberg connector; generic DDL-application frameworks forwarding all changes blindly.

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/a399e6c593525560. Report an issue: GitHub.

Appendix: source

Thrown at flink/v1.20/flink/src/main/java/org/apache/iceberg/flink/util/FlinkAlterTableUtil.java:149

      } else if (change instanceof TableChange.DropColumn) {
        TableChange.DropColumn dropColumn = (TableChange.DropColumn) change;
        pendingUpdate.deleteColumn(dropColumn.getColumnName());
      } else if (change instanceof TableChange.AddWatermark) {
        throw new UnsupportedOperationException("Unsupported table change: AddWatermark.");
      } else if (change instanceof TableChange.ModifyWatermark) {
        throw new UnsupportedOperationException("Unsupported table change: ModifyWatermark.");
      } else if (change instanceof TableChange.DropWatermark) {
        throw new UnsupportedOperationException("Unsupported table change: DropWatermark.");
      } else if (change instanceof TableChange.AddUniqueConstraint) {
        TableChange.AddUniqueConstraint addPk = (TableChange.AddUniqueConstraint) change;
        applyUniqueConstraint(pendingUpdate, addPk.getConstraint());
      } else if (change instanceof TableChange.ModifyUniqueConstraint) {
        TableChange.ModifyUniqueConstraint modifyPk = (TableChange.ModifyUniqueConstraint) change;
        applyUniqueConstraint(pendingUpdate, modifyPk.getNewConstraint());
      } else if (change instanceof TableChange.DropConstraint) {
        throw new UnsupportedOperationException("Unsupported table change: DropConstraint.");
      } else {
        throw new UnsupportedOperationException("Cannot apply unknown table change: " + change);
      }
    }
  }

  private static void applyAddColumn(UpdateSchema pendingUpdate, TableChange.AddColumn addColumn) {
    Column flinkColumn = addColumn.getColumn();
    Preconditions.checkArgument(
        FlinkCompatibilityUtil.isPhysicalColumn(flinkColumn),
        "Unsupported table change: Adding computed column %s.",
        flinkColumn.getName());

    Type icebergType = FlinkSchemaUtil.convert(flinkColumn.getDataType().getLogicalType());

    if (flinkColumn.getDataType().getLogicalType().isNullable()) {
      pendingUpdate.addColumn(
          flinkColumn.getName(), icebergType, flinkColumn.getComment().orElse(null));
    } else {
      pendingUpdate.addRequiredColumn(

View on GitHub (pinned to 86d9c8fc54)