apache/druid · error · QueryTimeoutException

Query [%s] timed out

Error message

Query [%s] timed out

What it means

The scan result iterator checks the query timeout on every next() call. When the current time reaches timeoutAt (derived from the query's timeout context), it throws QueryTimeoutException with the query id, aborting the scan. This enforces the 'timeout' query context parameter.

Source

Thrown at processing/src/main/java/org/apache/druid/query/scan/ScanQueryEngine.java:173

            final int batchSize = query.getBatchSize();
            return new Iterator<>()
            {
              private long offset = 0;

              @Override
              public boolean hasNext()
              {
                return !cursor.isDone() && offset < limit;
              }

              @Override
              public ScanResultValue next()
              {
                if (!hasNext()) {
                  throw new NoSuchElementException();
                }
                if (hasTimeout && System.currentTimeMillis() >= timeoutAt) {
                  throw new QueryTimeoutException(StringUtils.nonStrictFormat("Query [%s] timed out", query.getId()));
                }
                final long lastOffset = offset;
                final Object events;
                final ScanQuery.ResultFormat resultFormat = query.getResultFormat();
                if (ScanQuery.ResultFormat.RESULT_FORMAT_COMPACTED_LIST.equals(resultFormat)) {
                  events = rowsToCompactedList();
                } else if (ScanQuery.ResultFormat.RESULT_FORMAT_LIST.equals(resultFormat)) {
                  events = rowsToList();
                } else {
                  throw new UOE("resultFormat[%s] is not supported", resultFormat.toString());
                }
                responseContext.addRowScanCount(offset - lastOffset);
                return new ScanResultValue(
                    segment.getId() == null ? null : segment.getId().toString(),
                    allColumns,
                    events,
                    rowSignatureBuilder.build()
                );

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Increase the 'timeout' value in the query context
  2. Reduce scan scope: add filters, fewer columns, or lower limit/scanRows
  3. Scale the historical tier or improve segment (column) caching
  4. Catch QueryTimeoutException client-side and retry with a larger timeout

Example fix

// before
{"queryType":"scan","context":{"timeout":1000},...}
// after
{"queryType":"scan","context":{"timeout":300000},...}
Defensive patterns

Strategy: try-catch

Validate before calling

Long timeout = (Long) query.getContext().get("timeout");
if (timeout != null && timeout < expectedScanTimeMs) { /* raise or warn */ }

Try / catch

try {
  Sequence<ScanResultValue> seq = runner.run(QueryPlus.wrap(query), responseContext);
  seq.toList();
} catch (QueryTimeoutException e) {
  // retry with larger timeout or narrow the query
}

Prevention

When it happens

Trigger: Executing a scan query with a context timeout (hasTimeout true) whose execution exceeds the deadline; long scans over many segments with a small timeout value.

Common situations: Default low timeout in cluster config; large scans (millions of rows) exceeding client-set timeouts; slow disks or overloaded historicals making queries exceed the budget.

Understand the failure class

Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.

Related errors


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