{"record":{"id":"82fcc396916c1805","repo":"apache/druid","slug":"limit-of-d-rows-not-supported-for-priority-queue","errorCode":null,"errorMessage":"Limit of %,d rows not supported for priority queue strategy of time-ordering scan results","messagePattern":"Limit of %,d rows not supported for priority queue strategy of time-ordering scan results","errorType":"exception","errorClass":"UnsupportedOperationException (UOE)","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/scan/ScanQueryRunnerFactory.java","lineNumber":222,"sourceCode":"      }\n    };\n  }\n\n  /**\n   * Returns a sorted and limited copy of the provided {@param inputSequence}. Materializes the full sequence\n   * in memory before returning it. The amount of memory use is limited by the limit of the {@param scanQuery}.\n   */\n  @VisibleForTesting\n  Sequence<ScanResultValue> stableLimitingSort(\n      Sequence<ScanResultValue> inputSequence,\n      ScanQuery scanQuery,\n      List<Interval> intervalsOrdered\n  ) throws IOException\n  {\n    Comparator<ScanResultValue> comparator = scanQuery.getResultOrdering();\n\n    if (scanQuery.getScanRowsLimit() > Integer.MAX_VALUE) {\n      throw new UOE(\n          \"Limit of %,d rows not supported for priority queue strategy of time-ordering scan results\",\n          scanQuery.getScanRowsLimit()\n      );\n    }\n\n    // Converting the limit from long to int could theoretically throw an ArithmeticException but this branch\n    // only runs if limit < MAX_LIMIT_FOR_IN_MEMORY_TIME_ORDERING (which should be < Integer.MAX_VALUE)\n    int limit = Math.toIntExact(scanQuery.getScanRowsLimit());\n\n    final StableLimitingSorter<ScanResultValue> sorter = new StableLimitingSorter<>(comparator, limit);\n\n    Yielder<ScanResultValue> yielder = Yielders.each(inputSequence);\n\n    try {\n      boolean doneScanning = yielder.isDone();\n      // We need to scan limit elements and anything else in the last segment\n      int numRowsScanned = 0;\n      Interval finalInterval = null;","sourceCodeStart":204,"sourceCodeEnd":240,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/scan/ScanQueryRunnerFactory.java#L204-L240","documentation":"When a scan query uses resultOrdering (time-ordering), rows are buffered in an in-memory priority queue, so the scanRows limit must fit an int. A limit above Integer.MAX_VALUE cannot be allocated and is rejected with this UOE.","triggerScenarios":"Running a ScanQuery with getOrdering() set (descending time-ordering) and scanRowsLimit > 2147483647 (e.g. Long.MAX_VALUE, often from a default/unbounded limit configured for a non-time-ordered scan).","commonSituations":"Setting scanRowsLimit to Long.MAX_VALUE or a huge default in client code/SQL (LIMIT-less scan defaults) while also enabling ordering: 'descending' in SQL scan queries.","solutions":["Lower the scan query limit (scanRows / SQL LIMIT) to at most Integer.MAX_VALUE, ideally a realistic page size.","If you truly need unbounded results, remove the time-ordering ('descending' ordering) so the priority-queue strategy is not used and pagination via scanRowsOffset applies.","For large exports, iterate with ordered batches (limit + offset) instead of one giant ordered scan."],"exampleFix":"// before\nScanQuery q = new ScanQueryBuilder().order(Order.DESCENDING).limit(Long.MAX_VALUE).build();\n// after\nScanQuery q = new ScanQueryBuilder().order(Order.DESCENDING).limit(1_000_000).build();","handlingStrategy":"validation","validationCode":"if (query.getOrdering() != null && query.getScanRowsLimit() > Integer.MAX_VALUE) {\n  throw new IllegalArgumentException(\"scanRows limit too large for time-ordered scan\");\n}","typeGuard":"boolean limitFitsPriorityQueue(ScanQuery q) { return q.getScanRowsLimit() <= Integer.MAX_VALUE; }","tryCatchPattern":"try {\n  results = runner.run(QueryPlus.wrap(query), ctx).toList();\n} catch (UnsupportedOperationException e) {\n  if (e.getMessage().contains(\"not supported for priority queue\")) { query = query.withLimit(Integer.MAX_VALUE); }\n  else throw e;\n}","preventionTips":["Cap scanRows limits at Integer.MAX_VALUE.","Use batching (limit+offset) for large ordered exports.","Only enable ordering when a bounded limit is set."],"tags":["druid","scan-query","limit-exceeded"],"backgroundTag":"value-out-of-range","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"}