apache/iceberg · error · IllegalArgumentException

Unsupported operation mode: ${mode}

Error message

Unsupported operation mode: ${mode}

What it means

SparkRowLevelOperationBuilder.build() maps the resolved RowLevelOperationMode to an operation. Only COPY_ON_WRITE and MERGE_ON_READ are supported; any other mode value (e.g. a mode parsed from an unrecognized property string) hits the default branch and throws IllegalArgumentException.

Source

Thrown at spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/source/SparkRowLevelOperationBuilder.java:71

  SparkRowLevelOperationBuilder(
      SparkSession spark, Table table, String branch, RowLevelOperationInfo info) {
    this.spark = spark;
    this.table = table;
    this.branch = branch;
    this.info = info;
    this.mode = mode(table.properties(), info.command());
    this.isolationLevel = isolationLevel(table.properties(), info.command());
  }

  @Override
  public RowLevelOperation build() {
    switch (mode) {
      case COPY_ON_WRITE:
        return new SparkCopyOnWriteOperation(spark, table, branch, info, isolationLevel);
      case MERGE_ON_READ:
        return new SparkPositionDeltaOperation(spark, table, branch, info, isolationLevel);
      default:
        throw new IllegalArgumentException("Unsupported operation mode: " + mode);
    }
  }

  private RowLevelOperationMode mode(Map<String, String> properties, Command command) {
    String modeName;

    switch (command) {
      case DELETE:
        modeName = properties.getOrDefault(DELETE_MODE, DELETE_MODE_DEFAULT);
        break;
      case UPDATE:
        modeName = properties.getOrDefault(UPDATE_MODE, UPDATE_MODE_DEFAULT);
        break;
      case MERGE:
        modeName = properties.getOrDefault(MERGE_MODE, MERGE_MODE_DEFAULT);
        break;
      default:
        throw new IllegalArgumentException("Unsupported command: " + command);

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Check the table properties write.delete.mode, write.update.mode, write.merge.mode and set them only to 'copy-on-write' or 'merge-on-read'.
  2. Fix typos in the mode property value (mode comparison is by exact name).
  3. Align client and table Iceberg versions so RowLevelOperationMode.fromName cannot yield an unknown value.

Example fix

// before
ALTER TABLE t SET TBLPROPERTIES ('write.merge.mode' = 'mor');

// after
ALTER TABLE t SET TBLPROPERTIES ('write.merge.mode' = 'merge-on-read');
Defensive patterns

Strategy: validation

Validate before calling

for (String key : List.of("write.delete.mode", "write.update.mode", "write.merge.mode")) {
  String v = table.properties().get(key);
  if (v != null && !v.equals("copy-on-write") && !v.equals("merge-on-read")) {
    throw new IllegalArgumentException("Invalid " + key + ": " + v);
  }
}

Try / catch

try {
  RowLevelOperation op = builder.build();
} catch (IllegalArgumentException e) {
  logger.error("Check write.*.mode table properties", e);
}

Prevention

When it happens

Trigger: Calling build() when the mode resolved from table properties (write.delete.mode / write.update.mode / write.merge.mode) is not COPY_ON_WRITE or MERGE_ON_READ — typically RowLevelOperationMode.fromName returned null or an unknown mode.

Common situations: A typo'd or unsupported value for the write.*.mode table property, or a newer mode enum value from a different Iceberg version being passed to an older builder.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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