apache/iceberg · error · IllegalArgumentException
Unsorted order ID must be 0
Error message
Unsorted order ID must be 0
What it means
UnboundSortOrder.build() validates that an empty (unsorted) sort order must use order ID 0, because ID 0 is conventionally reserved for the unsorted order in the Iceberg spec. Passing a non-zero order ID with no sort fields is contradictory, so it throws IllegalArgumentException.
Source
Thrown at api/src/main/java/org/apache/iceberg/UnboundSortOrder.java:109
private Integer orderId = null;
private Builder() {}
Builder withOrderId(int newOrderId) {
this.orderId = newOrderId;
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;View on GitHub (pinned to 86d9c8fc54)
Solutions
- Use order ID 0 for an unsorted sort order.
- Omit the order ID (leave it null) and let the builder return the UNSORTED_ORDER singleton.
- Only assign a non-zero ID when the sort order actually has sort fields.
Example fix
// before
UnboundSortOrder order = SortOrder.builderFor(schema)
.withOrderId(3)
.build(); // no fields -> throws
// after
UnboundSortOrder order = SortOrder.builderFor(schema)
.withOrderId(0)
.build(); // unsorted Defensive patterns
Strategy: validation
Validate before calling
if (sortFields.isEmpty() && orderId != null && orderId != 0) {
throw new IllegalArgumentException("Unsorted sort order must use ID 0");
} Type guard
boolean isValidUnsorted = sortFields.isEmpty() ? (orderId == null || orderId == 0) : (orderId == null || orderId != 0);
Try / catch
try {
return builder.build();
} catch (IllegalArgumentException e) {
if (e.getMessage().contains("Unsorted order ID")) {
return builder.withOrderId(0).build();
}
throw e;
} Prevention
- Reserve order ID 0 for the unsorted order.
- Default order IDs: unsorted = 0, sorted starts at 1.
- Validate order ID vs. field count before building.
When it happens
Trigger: Building an UnboundSortOrder with zero fields (unsorted) while explicitly setting orderId to any value other than 0, e.g. SortOrder.builderFor(schema).withOrderId(5).build() with no sort fields added.
Common situations: Constructing sort orders programmatically when copying order IDs from table metadata; misinterpreting order IDs when deserializing or rebuilding sort orders; assigning fresh IDs to an intentionally unsorted order.
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
- Sort order ID 0 is reserved for unsorted order
- Field + field.name() + not found in source schema
- Provided schema:...Problems: (IllegalArgumentException with
- Cannot find field %s in %s
- Cannot delete element type from list:
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/1b47bb4fbf6e8866.
Report an issue: GitHub.