apache/druid · error · IllegalStateException (ISE)

Cannot execute query with orderBy %s

Error message

Cannot execute query with orderBy %s

What it means

verifyOrderByForNativeExecution enforces that the native scan engine can only execute queries whose ordering is fully determined by getTimeOrder(). If the query has explicit orderBy columns but timeOrder resolves to NONE, the native engine cannot honor them and throws ISE. This guards callers like getResultOrdering that need a single deterministic ordering.

Source

Thrown at processing/src/main/java/org/apache/druid/query/scan/ScanQuery.java:203

          }
        }
      }
    }

    this.maxRowsQueuedForOrdering = validateAndGetMaxRowsQueuedForOrdering();
    this.maxSegmentPartitionsOrderedInMemory = validateAndGetMaxSegmentPartitionsOrderedInMemory();
  }

  /**
   * Verifies that the ordering of a query is solely determined by {@link #getTimeOrder()}. Required to actually
   * execute queries, because {@link #getOrderBys()} is not yet understood by the query engines.
   *
   * @throws IllegalStateException if the ordering is not solely determined by {@link #getTimeOrder()}
   */
  public static void verifyOrderByForNativeExecution(final ScanQuery query)
  {
    if (query.getTimeOrder() == Order.NONE && !query.getOrderBys().isEmpty()) {
      throw new ISE("Cannot execute query with orderBy %s", query.getOrderBys());
    }
  }

  private Integer validateAndGetMaxRowsQueuedForOrdering()
  {
    final Integer maxRowsQueuedForOrdering =
        context().getInt(ScanQueryConfig.CTX_KEY_MAX_ROWS_QUEUED_FOR_ORDERING);
    Preconditions.checkArgument(
        maxRowsQueuedForOrdering == null || maxRowsQueuedForOrdering > 0,
        "maxRowsQueuedForOrdering must be greater than 0"
    );
    return maxRowsQueuedForOrdering;
  }

  private Integer validateAndGetMaxSegmentPartitionsOrderedInMemory()
  {
    final Integer maxSegmentPartitionsOrderedInMemory =
        context().getInt(ScanQueryConfig.CTX_KEY_MAX_SEGMENT_PARTITIONS_FOR_ORDERING);

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Set the query 'order' property (ascending/descending/descending-by-time) consistent with orderBy
  2. Remove the orderBy entries before native execution
  3. Route the query through the SQL layer which handles orderBy natively

Example fix

// before
ScanQuery q = new ScanQuery(...).withOrderByColumns(Collections.singletonList("col")); // no order set
ScanQuery.verifyOrderByForNativeExecution(q); // throws
// after
ScanQuery q = new ScanQuery(...).withOrderByColumns(Collections.singletonList("col")).withOrder(Order.ASCENDING);
ScanQuery.verifyOrderByForNativeExecution(q); // ok
Defensive patterns

Strategy: try-catch

Validate before calling

if (q.getTimeOrder() == ScanQuery.Order.NONE && !q.getOrderBys().isEmpty()) {
  q = q.withOrder(deduceOrder(q));
}

Try / catch

try {
  ScanQuery.verifyOrderByForNativeExecution(query);
} catch (IllegalStateException e) {
  // fall back to SQL layer or strip orderBy
}

Prevention

When it happens

Trigger: Calling ScanQuery.verifyOrderByForNativeExecution (directly or via getResultOrdering) with a query that has non-empty getOrderBys() and getTimeOrder() == Order.NONE.

Common situations: Internal/extension code running native scan queries on user queries containing orderBy without setting the order property; custom query tooling building ScanQuery programmatically with orderBy but no order.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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