apache/iceberg · error · IllegalArgumentException
Unsupported command: " + command
Error message
Unsupported command: " + command
What it means
SparkRowLevelOperationBuilder.mode(Command) only supports the DELETE, UPDATE, and MERGE commands; any other command has no row-level mode configuration and fails with IllegalArgumentException. The mode is resolved from table properties like write.delete.mode / write.update.mode / write.merge.mode.
Solutions
- Only invoke the builder for DELETE, UPDATE, or MERGE commands.
- Add a case for the new command in SparkRowLevelOperationBuilder if extending support.
- Check the Spark/Iceberg version pairing; a new Spark command may not yet be supported by this Iceberg version.
- Set the appropriate write.<command>.mode table property for supported commands.
Example fix
// before
RowLevelOperationMode mode = builder.mode(anyCommand);
// after
if (command instanceof Delete || command instanceof Update || command instanceof MergeIntoTable) {
RowLevelOperationMode mode = builder.mode(command);
} Defensive patterns
Strategy: validation
Validate before calling
Set<Command> supported = Set.of(Delete.class, Update.class, MergeIntoTable.class); if (!supported.contains(command.getClass())) throw new IllegalArgumentException("unsupported: " + command); Type guard
boolean supported = command instanceof Delete || command instanceof Update || command instanceof MergeIntoTable;
Try / catch
try { return builder.mode(command); } catch (IllegalArgumentException e) { return RowLevelOperationMode.COPY_ON_WRITE; } Prevention
- Only build row-level operations for DELETE/UPDATE/MERGE
- Keep Iceberg and Spark versions aligned
- Set write.<command>.mode table properties
When it happens
Trigger: mode() is called with a Command value outside the switch (DELETE/UPDATE/MERGE), e.g. a custom or future Spark Command type passed to the row-level operation builder.
Common situations: Third-party commands routed into Iceberg's row-level operation support; Spark version upgrades introducing new Command kinds; custom extensions calling the 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
- Cannot pass path based identifier to
- Cannot pass path based identifier to
- Cannot pass path based identifier to
- Invalid session catalog: sparkSessionCatalog
- Invalid session catalog
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/027b3242bf117b1c.
Report an issue: GitHub.
Appendix: source
Thrown at spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/source/SparkRowLevelOperationBuilder.java:94
new SparkPositionDeltaOperation(spark, table, snapshot, branch, info, isolationLevel);
};
}
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);
}
return RowLevelOperationMode.fromName(modeName);
}
private IsolationLevel isolationLevel(Map<String, String> properties, Command command) {
String levelName;
switch (command) {
case DELETE:
levelName = properties.getOrDefault(DELETE_ISOLATION_LEVEL, DELETE_ISOLATION_LEVEL_DEFAULT);
break;
case UPDATE:
levelName = properties.getOrDefault(UPDATE_ISOLATION_LEVEL, UPDATE_ISOLATION_LEVEL_DEFAULT);
break;
case MERGE:
levelName = properties.getOrDefault(MERGE_ISOLATION_LEVEL, MERGE_ISOLATION_LEVEL_DEFAULT);
break;View on GitHub (pinned to 86d9c8fc54)