apache/iceberg · error · IllegalArgumentException
Sort order ID 0 is reserved for unsorted order
Error message
Sort order ID 0 is reserved for unsorted order
What it means
UnboundSortOrder.build() rejects sort order ID 0 for any sort order that has fields, because the Iceberg spec reserves ID 0 exclusively for the unsorted order. A sort order with fields must be assigned ID 1 or higher (or left null, defaulting to 1).
Source
Thrown at api/src/main/java/org/apache/iceberg/UnboundSortOrder.java:115
return this;
}
Builder addSortField(
String transformAsString, int sourceId, SortDirection direction, NullOrder nullOrder) {
fields.add(new UnboundSortField(transformAsString, sourceId, direction, nullOrder));
return this;
}
UnboundSortOrder build() {
if (fields.isEmpty()) {
if (orderId != null && orderId != 0) {
throw new IllegalArgumentException("Unsorted order ID must be 0");
}
return UNSORTED_ORDER;
}
if (orderId != null && orderId == 0) {
throw new IllegalArgumentException("Sort order ID 0 is reserved for unsorted order");
}
// default ID to 1 as 0 is reserved for unsorted order
int actualOrderId = orderId != null ? orderId : 1;
return new UnboundSortOrder(actualOrderId, fields);
}
}
static class UnboundSortField {
private final Transform<?, ?> transform;
private final int sourceId;
private final SortDirection direction;
private final NullOrder nullOrder;
private UnboundSortField(
String transformAsString, int sourceId, SortDirection direction, NullOrder nullOrder) {
this.transform = Transforms.fromString(transformAsString);
this.sourceId = sourceId;View on GitHub (pinned to 86d9c8fc54)
Solutions
- Assign an order ID of 1 or greater when sort fields are present.
- Omit the order ID so it defaults to 1.
- Reserve ID 0 only for the unsorted order.
Example fix
// before
SortOrder.builderFor(schema).asc("id").withOrderId(0).build();
// after
SortOrder.builderFor(schema).asc("id").withOrderId(1).build(); Defensive patterns
Strategy: validation
Validate before calling
if (!sortFields.isEmpty() && orderId != null && orderId == 0) {
throw new IllegalArgumentException("Sort order ID 0 is reserved for unsorted");
} Type guard
boolean isValidSorted = sortFields.isEmpty() || orderId == null || orderId > 0;
Try / catch
try {
return builder.build();
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("reserved for unsorted")) {
return builder.build(); // let ID default to 1
}
throw e;
} Prevention
- Never hardcode order ID 0 for sorted orders.
- Omit the order ID to let it default to 1.
- Remember: 0 = unsorted, 1+ = sorted in the Iceberg spec.
When it happens
Trigger: Building an UnboundSortOrder with one or more sort fields while explicitly calling withOrderId(0), e.g. SortOrder.builderFor(schema).asc("id").withOrderId(0).build().
Common situations: Hand-assigning order IDs when creating new sort orders; copying ID 0 from an unsorted table's metadata into a new sorted order; off-by-one thinking that IDs start at 0.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Unsorted order ID must be 0
- Invalid distribution mode: %s
- this.getClass().getName() + " does not implement create with
- Visitor %s does not support non-reference: %s
- Cannot create expression literal from %s: %s
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/4bad2a0fc22d8af5.
Report an issue: GitHub.