apache/iceberg · error · IllegalArgumentException

Unsupported isolation level: ${isolationLevel}

Error message

Unsupported isolation level: ${isolationLevel}

What it means

SparkWrite.commit() dispatches to a commit strategy based on the configured IsolationLevel. Only SERIAL and SNAPSHOT isolation levels are recognized for branch writes; any other value throws IllegalArgumentException, indicating a corrupted or unknown isolation-level configuration.

Source

Thrown at spark/v3.5/spark/src/main/java/org/apache/iceberg/spark/source/SparkWrite.java:460

      int numAddedFiles = 0;
      for (DataFile file : files(messages)) {
        numAddedFiles += 1;
        overwriteFiles.addFile(file);
      }

      // the scan may be null if the optimizer replaces it with an empty relation (e.g. false cond)
      // no validation is needed in this case as the command does not depend on the table state
      if (scan != null) {
        switch (isolationLevel) {
          case SERIALIZABLE:
            commitWithSerializableIsolation(overwriteFiles, numOverwrittenFiles, numAddedFiles);
            break;
          case SNAPSHOT:
            commitWithSnapshotIsolation(overwriteFiles, numOverwrittenFiles, numAddedFiles);
            break;
          default:
            throw new IllegalArgumentException("Unsupported isolation level: " + isolationLevel);
        }

      } else {
        commitOperation(
            overwriteFiles,
            String.format(
                Locale.ROOT, "overwrite with %d new data files (no validation)", numAddedFiles));
      }
    }

    private void commitWithSerializableIsolation(
        OverwriteFiles overwriteFiles, int numOverwrittenFiles, int numAddedFiles) {
      Long scanSnapshotId = scan.snapshotId();
      if (scanSnapshotId != null) {
        overwriteFiles.validateFromSnapshot(scanSnapshotId);
      }

      Expression conflictDetectionFilter = conflictDetectionFilter();

View on GitHub (pinned to 86d9c8fc54)

Solutions

  1. Set write.*.isolation-level table properties only to 'serializable' or 'snapshot'.
  2. Fix typos in the isolation-level property so IsolationLevel.fromName resolves to a known value.
  3. Align Iceberg versions so the write path knows every IsolationLevel value.

Example fix

// before
ALTER TABLE t SET TBLPROPERTIES ('write.merge.isolation-level' = 'serial');

// after
ALTER TABLE t SET TBLPROPERTIES ('write.merge.isolation-level' = 'serializable');
Defensive patterns

Strategy: validation

Validate before calling

String level = table.properties().getOrDefault("write.merge.isolation-level", "serializable");
if (!level.equals("serializable") && !level.equals("snapshot")) {
  throw new IllegalArgumentException("Invalid isolation level: " + level);
}

Try / catch

try {
  write.commit();
} catch (IllegalArgumentException e) {
  logger.error("Bad write.*.isolation-level property", e);
}

Prevention

When it happens

Trigger: Committing a write whose isolationLevel resolved (from write.*.isolation-level table properties via IsolationLevel.fromName) to something other than SERIAL or SNAPSHOT — typically an unrecognized string mapping to an unexpected enum/null value.

Common situations: A typo'd isolation-level property value, a new IsolationLevel added in a newer version used with an older write path, or programmatic construction with a bogus value.

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