prestodb/presto · error · IllegalArgumentException
Unable to parse sort field: [%s]
Error message
Unable to parse sort field: [%s]
What it means
parseSortField applies a regex (PATTERN) to each sorted_by entry to extract identifier, ordering (ASC/DESC), optional null ordering, and transform. Entries that don't match the grammar throw IllegalArgumentException("Unable to parse sort field: [%s]") which surfaces from parseSortFields. It enforces the strict syntax of the sorted_by property.
Source
Thrown at presto-iceberg/src/main/java/com/facebook/presto/iceberg/SortFieldUtils.java:84
for (SortField field : sortOrder.fields()) {
if (!baseColumnFieldIds.contains(field.sourceId())) {
throw new PrestoException(COLUMN_NOT_FOUND, "Column not found: " + schema.findColumnName(field.sourceId()));
}
}
return sortOrder;
}
public static void parseSortFields(SortOrderBuilder<?> sortOrderBuilder, List<String> fields)
{
fields.forEach(field -> parseSortField(sortOrderBuilder, field));
}
private static void parseSortField(SortOrderBuilder<?> builder, String field)
{
Matcher matcher = PATTERN.matcher(field);
if (!matcher.matches()) {
throw new IllegalArgumentException(format("Unable to parse sort field: [%s]", field));
}
String columnName = fromIdentifierToColumn(matcher.group("identifier"));
boolean ascending;
String ordering = firstNonNull(matcher.group("ordering"), "ASC").toUpperCase(Locale.ENGLISH);
switch (ordering) {
case "ASC":
ascending = true;
break;
case "DESC":
ascending = false;
break;
default:
throw new IllegalStateException("Unexpected ordering value: " + ordering);
}
String nullOrderDefault = ascending ? "FIRST" : "LAST";View on GitHub (pinned to 55bb57d202)
Solutions
- Rewrite each entry in the accepted form: identifier [ASC|DESC] with optional transform, e.g. 'col ASC', 'truncate(10, col) DESC'
- Remove surrounding quotes/spaces accidentally included inside the array element
- Replace complex expressions with plain column names plus Iceberg transforms
- Create without sorted_by, then add the property incrementally, testing one field at a time to isolate the malformed entry
Example fix
// before WITH (sorted_by = ARRAY['LOWER(name) ASC']) // after WITH (sorted_by = ARRAY['name ASC'])
Defensive patterns
Strategy: validation
Validate before calling
Pattern P = Pattern.compile("(?<identifier>\\S+?)(\\s+(?<ordering>ASC|DESC))?(\\s+NULLS\\s+(?<nulls>FIRST|LAST))?\\s*$");
for (String f : sortedBy) {
if (!P.matcher(f).matches()) throw new IllegalArgumentException("Unable to parse sort field: [" + f + "]");
} Type guard
boolean isValidSortField(String f) {
return f != null && f.matches("^\\S+(\\s+(ASC|DESC))?(\\s+NULLS\\s+(FIRST|LAST))?\\s*$");
} Try / catch
try { /* CREATE TABLE with sorted_by */ } catch (Exception e) {
if (e.getMessage() != null && e.getMessage().startsWith("Unable to parse sort field")) {
// offending entry is inside the brackets; fix syntax and retry
} else { throw e; }
} Prevention
- Use only the grammar: identifier [ASC|DESC] [NULLS FIRST|LAST], with optional Iceberg transforms
- Do not pass SQL expressions or ORDER BY clauses as sorted_by entries
- Trim whitespace and avoid stray quotes inside array elements
- Test each sorted_by entry individually before final DDL
When it happens
Trigger: CREATE TABLE ... WITH (sorted_by = ARRAY[...]) containing an entry that fails the regex — e.g. empty string, missing identifier, malformed transform like 'bucket(abc, col)', stray characters, or a completely free-form sort expression.
Common situations: Passing SQL ORDER BY expressions instead of the identifier grammar; quoting errors leaving extra quotes in the string; whitespace or case variants the pattern rejects; copying syntax from other engines (e.g. Spark CLUSTER BY).
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
AI-assisted analysis of prestodb/presto@55bb57d202 (2026-09-04).
Data as JSON: /api/errors/23ca200a389db8fd.
Report an issue: GitHub.