apache/druid · error · IllegalStateException

Null cursor factory found. Probably trying to issue a query

Error message

Null cursor factory found. Probably trying to issue a query against a segment being memory unmapped.

What it means

GroupByQuery processing requires a non-null CursorFactory to read segment data. The engine throws this ISE when the cursor factory supplied to validateForProcess is null, which in Druid indicates the segment's underlying data was memory-unmapped (dropped) while a query was in flight. It is a guard against querying a segment that no longer has readable data.

Source

Thrown at processing/src/main/java/org/apache/druid/query/groupby/GroupingEngine.java:530

   */
  public Sequence<ResultRow> processCursorHolder(
      GroupByQuery query,
      CursorFactory cursorFactory,
      CursorHolder cursorHolder,
      @Nullable TimeBoundaryInspector timeBoundaryInspector,
      NonBlockingPool<ByteBuffer> bufferPool,
      @Nullable GroupByQueryMetrics groupByQueryMetrics
  )
  {
    validateForProcess(query, cursorFactory);
    final CursorBuildSpec buildSpec = makeCursorBuildSpec(query, groupByQueryMetrics);
    return processWithCursorHolder(query, cursorFactory, cursorHolder, timeBoundaryInspector, bufferPool, buildSpec);
  }

  private static void validateForProcess(GroupByQuery query, @Nullable CursorFactory cursorFactory)
  {
    if (cursorFactory == null) {
      throw new ISE(
          "Null cursor factory found. Probably trying to issue a query against a segment being memory unmapped."
      );
    }

    final List<Interval> intervals = query.getQuerySegmentSpec().getIntervals();
    if (intervals.size() != 1) {
      throw new IAE("Should only have one interval, got[%s]", intervals);
    }
  }

  private Sequence<ResultRow> processWithCursorHolder(
      GroupByQuery query,
      CursorFactory cursorFactory,
      CursorHolder cursorHolder,
      @Nullable TimeBoundaryInspector timeBoundaryInspector,
      NonBlockingPool<ByteBuffer> bufferPool,
      CursorBuildSpec buildSpec
  )

View on GitHub (pinned to 9b90983fd2)

Solutions

  1. Retry the query; the segment is usually re- or already re-loaded by a different server
  2. Check cluster segment availability/coordination logs around the failure time for drop/load races
  3. Increase query retries/timeouts so transient unmapping is absorbed (RetryQueryException handling)
  4. Reduce segment churn: adjust load/drop periods, replica counts, or coordinator balancing thresholds
  5. If triggered by code, never pass null cursorFactory; validate segment state before issuing the query

Example fix

// before
Sequence<ResultRow> results = engine.process(query, null, ...);
// after
if (cursorFactory == null) {
  throw new QueryInterruptedException(new ResourceLimitException("Segment temporarily unavailable; retry"));
}
Sequence<ResultRow> results = engine.process(query, cursorFactory, ...);
Defensive patterns

Strategy: retry

Validate before calling

if (cursorFactory == null) { throw new RetryQueryException("segment unmapped; retry"); }

Type guard

boolean isQueryable(CursorFactory f) { return f != null; }

Try / catch

try { results = engine.process(query, cursorFactory, ...); } catch (ISE e) { if (e.getMessage().contains("Null cursor factory")) { retryQuery(query); } else { throw e; } }

Prevention

When it happens

Trigger: Calling GroupingEngine.process / makeCursorHolderAsync / processCursorHolder with a null CursorFactory, typically when the segment backing the query is unmapped (e.g. historical segment dropped, cache entry evicted, or real-time task swapped the segment) between query start and execution.

Common situations: Historical servers loading/dropping segments under load; queries racing with segment hand-off in streaming ingestion; over-aggressive cache unloading; coordination rebalancing mid-query.

Related errors


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