apache/druid · error · IllegalStateException

Pre-sorted data required, rows

Error message

Pre-sorted data required, rows[%s] and [%s] were not in order

What it means

DefaultGroupPartitioner.computeGroupings assigns group IDs by scanning consecutive rows and requires rows pre-sorted by the grouping columns. If it detects two adjacent rows where the previous row compares greater than the current one (comparison > 0) while their group values are equal, the pre-sort invariant is violated and Druid throws ISE.

Solutions

  1. Ensure the input is sorted by the grouping columns before calling computeGroupings
  2. Insert/repair an upstream sort stage
  3. Verify the row accessor's comparison is consistent with the actual sort order used
Defensive patterns

Strategy: validation

Validate before calling

// verify adjacent rows are ordered before grouping
for (int i = 1; i < accessor.numRows(); i++) {
  if (accessor.compareRows(i - 1, i) > 0) {
    throw new IllegalStateException("rows not pre-sorted at " + i);
  }
}

Try / catch

try {
  groupings = partitioner.computeGroupings(accessor);
} catch (IllegalStateException e) {
  if (e.getMessage().contains("Pre-sorted data required")) {
    accessor = sort(accessor);
    groupings = partitioner.computeGroupings(accessor);
  } else throw e;
}

Prevention

When it happens

Trigger: computeGroupings is called on data whose accessor.compareRows(i-1, i) returns a positive value for adjacent rows, i.e. input rows are out of order relative to the grouping keys.

Common situations: A downstream/window operator consuming unsorted input; a sort stage removed or broken upstream; custom adapter feeding rows in arbitrary order.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/7c39acd4fadfb87e. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/query/rowsandcols/semantic/DefaultGroupPartitioner.java:64

    for (String column : columns) {
      final Column theCol = rac.findColumn(column);
      if (theCol == null) {
        // The column doesn't exist.  In this case, we assume it's always the same value: null.  If it's always
        // the same, then it doesn't impact grouping at all and can be entirely skipped.
        continue;
      }
      final ColumnAccessor accessor = theCol.toAccessor();

      int currGroup = 0;
      int prevGroupVal = 0;
      for (int i = 1; i < retVal.length; ++i) {
        if (retVal[i] == prevGroupVal) {
          int comparison = accessor.compareRows(i - 1, i);
          if (comparison == 0) {
            retVal[i] = currGroup;
            continue;
          } else if (comparison > 0) { // "greater than"
            throw new ISE("Pre-sorted data required, rows[%s] and [%s] were not in order", i - 1, i);
          } // the 3rd condition ("less than") means create a new group, so let it fall through
        }

        // We have a new group, so walk things forward.
        prevGroupVal = retVal[i];
        retVal[i] = ++currGroup;
      }
    }

    return retVal;
  }
}

View on GitHub (pinned to 9b90983fd2)