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
- Retry the query; the segment is usually re- or already re-loaded by a different server
- Check cluster segment availability/coordination logs around the failure time for drop/load races
- Increase query retries/timeouts so transient unmapping is absorbed (RetryQueryException handling)
- Reduce segment churn: adjust load/drop periods, replica counts, or coordinator balancing thresholds
- 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
- Wrap query submission in retry logic for transient segment-unmapped errors
- Monitor segment load/drop churn in the cluster
- Pin queries to replicas with loaded segments (use segment availability checks)
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
- Null queryRunner! Looks to be some segment unmapping action
- Could not create group mapping [%s] due to concurrent update
- Could not delete group mapping [%s] due to concurrent update
- Could not create role [%s] due to concurrent update contenti
- Could not delete role [%s] due to concurrent update contenti
AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07).
Data as JSON: /api/errors/b9f148525868ec48.
Report an issue: GitHub.