apache/beam · error · IllegalArgumentException
Could not parse sort modifiers
Error message
Could not parse sort modifiers '{}' in '{}'. Expected: [asc|desc] [nulls first|nulls last]. What it means
SortOrderUtils.parse builds an Iceberg SortOrder from a string spec like 'id asc nulls last'. When the modifier suffix of a field expression (e.g. 'desc nulls first') fails the MODIFIERS regex, it throws IllegalArgumentException with this message, echoing the bad modifiers and the full field expression.
Solutions
- Use only the accepted tokens: field name optionally followed by 'asc' or 'desc', optionally followed by 'nulls first' or 'nulls last' (e.g. 'id desc nulls last')
- Remove extra words/commas so each field spec is a single expression ('id asc', not 'ORDER BY id asc')
- Check exact spelling and casing expected by SortOrderUtils, and trim stray whitespace before parsing
Example fix
// before String spec = "id DESC NULLS FIRST"; // rejected by MODIFIERS regex // after String spec = "id desc nulls first";
Defensive patterns
Strategy: validation
Validate before calling
private static final java.util.regex.Pattern MODIFIERS_OK =
java.util.regex.Pattern.compile("(asc|desc)?(\\s+nulls\\s+(first|last))?\\s*");
void checkSortField(String field) {
String[] parts = field.trim().split("\\s+", 2);
String rest = parts.length > 1 ? parts[1] : "";
if (!rest.isEmpty() && !MODIFIERS_OK.matcher(rest).matches()) {
throw new IllegalArgumentException("Bad sort modifiers in: " + field);
}
} Type guard
boolean isValidSortField(String field) {
return field != null && field.matches("\\w+\\s*(asc|desc)?(\\s+nulls\\s+(first|last))?\\s*");
} Try / catch
try {
SortOrder order = SortOrderUtils.parsed(spec);
} catch (IllegalArgumentException e) {
// log e.getMessage(), fix config or fall back to default sort order
} Prevention
- Validate sort spec strings against the `[asc|desc] [nulls first|nulls last]` grammar before submitting pipeline options
- Avoid pasting SQL ORDER BY clauses verbatim into sort order configs
- Trim and normalize whitespace/casing of sort spec strings
- Add unit tests covering each modifier form (asc/desc x nulls first/nulls last)
When it happens
Trigger: Passing a sort field string whose direction/null-order suffix doesn't match `[asc|desc] [nulls first|nulls last]` — e.g. 'name ascending', 'id DESC NULLS FIRST' with case handling the regex rejects, extra tokens ('id desc nulls first extra'), or misspellings ('nuls last').
Common situations: Configuring IcebergIO sort orders via pipeline options/CLI where whitespace or typos slip in; copy-pasted SQL-style ORDER BY clauses ('ORDER BY' keyword, comma handling) pasted into the spec string; locale or case differences.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- SortOrder with id not found in sortOrders map
- Table of class does not implement HasTableOperations
- 2xx codes should not be exceptions. Got status code
- Adding required columns is not yet supported. Encountered…
- ApproximateUnique.PerKey needs an estimation error between…
AI-assisted analysis of apache/beam@12126d8942 (2026-09-13).
Data as JSON: /api/errors/94009b2831828168.
Report an issue: GitHub.
Appendix: source
Thrown at sdks/java/io/iceberg/src/main/java/org/apache/beam/sdk/io/iceberg/SortOrderUtils.java:83
}
}
return builder.build();
}
private static ParsedSortField parse(String field) {
field = field.trim();
int splitAt = findTopLevelWhitespace(field);
String termStr = (splitAt < 0 ? field : field.substring(0, splitAt)).trim();
String rest = (splitAt < 0 ? "" : field.substring(splitAt)).trim();
Term term = PartitionUtils.toIcebergTerm(termStr);
boolean ascending = true;
@Nullable NullOrder nullOrder = null;
if (!rest.isEmpty()) {
Matcher matcher = MODIFIERS.matcher(rest);
if (!matcher.matches()) {
throw new IllegalArgumentException(
"Could not parse sort modifiers '"
+ rest
+ "' in '"
+ field
+ "'. Expected: [asc|desc] [nulls first|nulls last].");
}
String dir = matcher.group("dir");
if (dir != null) {
ascending = dir.toLowerCase(Locale.ROOT).equals("asc");
}
String nulls = matcher.group("nulls");
if (nulls != null) {
nullOrder =
nulls.toLowerCase(Locale.ROOT).equals("first")
? NullOrder.NULLS_FIRST
: NullOrder.NULLS_LAST;
}
}View on GitHub (pinned to 12126d8942)