{"record":{"id":"ab68a84f2467a561","repo":"apache/druid","slug":"orderingstomerge-is-empty","errorCode":null,"errorMessage":"orderingsToMerge is empty","messagePattern":"orderingsToMerge is empty","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/segment/Metadata.java","lineNumber":368,"sourceCode":"           \", timestampSpec=\" + timestampSpec +\n           \", queryGranularity=\" + queryGranularity +\n           \", rollup=\" + rollup +\n           \", ordering=\" + ordering +\n           \", projections=\" + projections +\n           \", clusteredBaseTable=\" + clusteredBaseTable +\n           '}';\n  }\n\n  /**\n   * Merge {@link #getOrdering()} from different metadatas.\n   *\n   * When an input sort order is null, we assume it is {@link Cursors#ascendingTimeOrder()}, as this was the only\n   * sort order possible prior to the introduction of the \"ordering\" field.\n   */\n  public static List<OrderBy> mergeOrderings(List<List<OrderBy>> orderingsToMerge)\n  {\n    if (orderingsToMerge.isEmpty()) {\n      throw new IAE(\"orderingsToMerge is empty\");\n    }\n\n    final List<OrderBy> mergedOrdering = new ArrayList<>();\n\n    while (true) {\n      final int position = mergedOrdering.size();\n      OrderBy orderBy = null;\n\n      // Iterate through each sort order, check that the columns at \"position\" are all the same. If not, return\n      // the mergedOrdering as-is.\n      for (List<OrderBy> ordering : orderingsToMerge) {\n        if (ordering == null) {\n          // null ordering is treated as [__time].\n          ordering = Cursors.ascendingTimeOrder();\n        }\n\n        if (position < ordering.size()) {\n          if (orderBy == null) {","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/segment/Metadata.java#L350-L386","documentation":"Metadata.mergeOrderings throws IAE when given an empty list of per-segment orderings to merge. Merging sort-order metadata requires at least one input ordering; with none, no merged OrderBy list can be computed.","triggerScenarios":"Calling Metadata.mergeOrderings(Collections.emptyList()) or mergedOrdering over segments where no segment has ordering metadata (e.g. legacy segments) without defaulting the input lists.","commonSituations":"Custom tooling reading/merging segment metadata for all-null or legacy segments built before 'ordering' was introduced; compaction code aggregating metadata from empty segment sets.","solutions":["Guard the call site and return a default (e.g. Cursors.ascendingTimeOrder) when the list is empty","Pass a default ordering list for legacy segments instead of an empty list","Only invoke mergeOrderings when at least one segment supplies ordering metadata"],"exampleFix":"// before\nList<OrderBy> merged = Metadata.mergeOrderings(orderingsToMerge);\n// after\nList<OrderBy> merged = orderingsToMerge.isEmpty()\n    ? Collections.singletonList(OrderBy.ascendingTimeOrder())\n    : Metadata.mergeOrderings(orderingsToMerge);","handlingStrategy":"validation","validationCode":"if (orderingsToMerge == null || orderingsToMerge.isEmpty()) { return defaultOrdering(); }","typeGuard":null,"tryCatchPattern":"try { Metadata.mergeOrderings(list); } catch (IAE e) { if (e.getMessage().contains(\"orderingsToMerge is empty\")) { /* use default time-ordering */ } else throw e; }","preventionTips":["Default legacy/null ordering metadata to ascendingTimeOrder before merging","Skip mergeOrderings when no segment carries ordering metadata","Collect metadata across all segments before merging"],"tags":["druid","segment-metadata","empty-input"],"backgroundTag":"empty-required-field","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}