apache/iceberg · error · IllegalArgumentException

Cannot mix Zorder and Hilbert sort expressions:

Error message

Cannot mix Zorder and Hilbert sort expressions: 

What it means

parseSortOrder parses the sort_order option of the rewrite_data_files procedure into three buckets: Zorder terms, Hilbert terms, and plain identity sort fields. Zorder and Hilbert are two distinct space-filling-curve techniques, and Iceberg can only build one curve per rewrite, so mixing them in a single sort_order expression is rejected outright.

Solutions

  1. Remove either the zorder(...) or the hilbert(...) terms so the sort_order uses only one curve family.
  2. If both orderings are needed, run rewrite_data_files twice as separate operations, each with a single curve style.
  3. Pick the curve appropriate for your query predicates (hilbert generally has better locality; zorder is the long-standing default).

Example fix

// before
.option("sort_order", "zorder(a, b), hilbert(c)")
// after
.option("sort_order", "hilbert(a, b, c)")
Defensive patterns

Strategy: validation

Validate before calling

boolean hasZ = sortOrder.contains("zorder(");
boolean hasH = sortOrder.contains("hilbert(");
if (hasZ && hasH) throw new IllegalArgumentException("sort_order may use only one of zorder/hilbert");

Try / catch

try {
    action.option("sort_order", sortOrder).execute();
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Cannot mix Zorder and Hilbert")) { /* sanitize config */ }
}

Prevention

When it happens

Trigger: Calling rewrite_data_files with sort_order containing both zorder(...) and hilbert(...) expressions, e.g. sort_order='zorder(a), hilbert(b)'. Raised inside parseSortOrder, which checkAndApplyStrategy invokes when strategy='sort'.

Common situations: Users concatenating multiple sort recommendations into one option string; incrementally editing a config where one team added zorder columns and another added hilbert columns; copying sort_order from docs showing each style separately and merging them.

Related errors


AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12). Data as JSON: /api/errors/4ce4715c18206580. Report an issue: GitHub.

Appendix: source

Thrown at spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/procedures/RewriteDataFilesProcedure.java:205

  private void parseSortOrder(
      String sortOrderString,
      List<Zorder> zOrderTerms,
      List<Hilbert> hilbertTerms,
      List<ExtendedParser.RawOrderField> sortOrderFields) {
    ExtendedParser.parseSortOrder(spark(), sortOrderString)
        .forEach(
            field -> {
              if (field.term() instanceof Zorder) {
                zOrderTerms.add((Zorder) field.term());
              } else if (field.term() instanceof Hilbert) {
                hilbertTerms.add((Hilbert) field.term());
              } else {
                sortOrderFields.add(field);
              }
            });

    if (!zOrderTerms.isEmpty() && !hilbertTerms.isEmpty()) {
      throw new IllegalArgumentException(
          "Cannot mix Zorder and Hilbert sort expressions: " + sortOrderString);
    }

    if ((!zOrderTerms.isEmpty() || !hilbertTerms.isEmpty()) && !sortOrderFields.isEmpty()) {
      throw new IllegalArgumentException(
          "Cannot mix identity sort columns and a Zorder or Hilbert sort expression: "
              + sortOrderString);
    }
  }

  private RewriteDataFilesSparkAction applySortStrategy(
      RewriteDataFilesSparkAction action,
      List<Zorder> zOrderTerms,
      List<Hilbert> hilbertTerms,
      List<ExtendedParser.RawOrderField> sortOrderFields,
      Schema schema) {
    if (!zOrderTerms.isEmpty()) {
      String[] columnNames =

View on GitHub (pinned to 86d9c8fc54)