{"record":{"id":"4ce4715c18206580","repo":"apache/iceberg","slug":"cannot-mix-zorder-and-hilbert-sort-expressions","errorCode":null,"errorMessage":"Cannot mix Zorder and Hilbert sort expressions: ","messagePattern":"Cannot mix Zorder and Hilbert sort expressions: ","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/procedures/RewriteDataFilesProcedure.java","lineNumber":205,"sourceCode":"  private void parseSortOrder(\n      String sortOrderString,\n      List<Zorder> zOrderTerms,\n      List<Hilbert> hilbertTerms,\n      List<ExtendedParser.RawOrderField> sortOrderFields) {\n    ExtendedParser.parseSortOrder(spark(), sortOrderString)\n        .forEach(\n            field -> {\n              if (field.term() instanceof Zorder) {\n                zOrderTerms.add((Zorder) field.term());\n              } else if (field.term() instanceof Hilbert) {\n                hilbertTerms.add((Hilbert) field.term());\n              } else {\n                sortOrderFields.add(field);\n              }\n            });\n\n    if (!zOrderTerms.isEmpty() && !hilbertTerms.isEmpty()) {\n      throw new IllegalArgumentException(\n          \"Cannot mix Zorder and Hilbert sort expressions: \" + sortOrderString);\n    }\n\n    if ((!zOrderTerms.isEmpty() || !hilbertTerms.isEmpty()) && !sortOrderFields.isEmpty()) {\n      throw new IllegalArgumentException(\n          \"Cannot mix identity sort columns and a Zorder or Hilbert sort expression: \"\n              + sortOrderString);\n    }\n  }\n\n  private RewriteDataFilesSparkAction applySortStrategy(\n      RewriteDataFilesSparkAction action,\n      List<Zorder> zOrderTerms,\n      List<Hilbert> hilbertTerms,\n      List<ExtendedParser.RawOrderField> sortOrderFields,\n      Schema schema) {\n    if (!zOrderTerms.isEmpty()) {\n      String[] columnNames =","sourceCodeStart":187,"sourceCodeEnd":223,"githubUrl":"https://github.com/apache/iceberg/blob/86d9c8fc543e7c56c9f624eb725f76c9baff9570/spark/v4.1/spark/src/main/java/org/apache/iceberg/spark/procedures/RewriteDataFilesProcedure.java#L187-L223","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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.","solutions":["Remove either the zorder(...) or the hilbert(...) terms so the sort_order uses only one curve family.","If both orderings are needed, run rewrite_data_files twice as separate operations, each with a single curve style.","Pick the curve appropriate for your query predicates (hilbert generally has better locality; zorder is the long-standing default)."],"exampleFix":"// before\n.option(\"sort_order\", \"zorder(a, b), hilbert(c)\")\n// after\n.option(\"sort_order\", \"hilbert(a, b, c)\")","handlingStrategy":"validation","validationCode":"boolean hasZ = sortOrder.contains(\"zorder(\");\nboolean hasH = sortOrder.contains(\"hilbert(\");\nif (hasZ && hasH) throw new IllegalArgumentException(\"sort_order may use only one of zorder/hilbert\");","typeGuard":null,"tryCatchPattern":"try {\n    action.option(\"sort_order\", sortOrder).execute();\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Cannot mix Zorder and Hilbert\")) { /* sanitize config */ }\n}","preventionTips":["Centralize the sort_order string as a single configurable constant per table","Validate sort_order content before invoking rewrite_data_files","Document that one curve family per rewrite is the limit"],"tags":["spark","sort-order","config-validation"],"backgroundTag":"mutually-exclusive-options","analyzedSha":"86d9c8fc543e7c56c9f624eb725f76c9baff9570","analyzedAt":"2026-09-12T00:46:39.097Z","contentChangedAt":"2026-09-12T00:46:39.097Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}