{"record":{"id":"782509de058bc6bc","repo":"apache/druid","slug":"null-cursor-factory-found-probably-trying-to-issu","errorCode":null,"errorMessage":"Null cursor factory found. Probably trying to issue a query against a segment being memory unmapped.","messagePattern":"Null cursor factory found\\. Probably trying to issue a query against a segment being memory unmapped\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"error","filePath":"multi-stage-query/src/main/java/org/apache/druid/msq/querykit/scan/ScanQueryFrameProcessor.java","lineNumber":398,"sourceCode":"      return ReturnOrAwait.runAgain();\n    }\n  }\n\n  @Override\n  protected ReturnOrAwait<Unit> runWithInputChannel(\n      final ReadableFrameChannel inputChannel,\n      final FrameReader inputFrameReader\n  ) throws IOException\n  {\n    if (cursor == null || cursor.isDone()) {\n      if (inputChannel.canRead()) {\n        final Frame frame = inputChannel.readFrame();\n        final FrameSegment frameSegment = new FrameSegment(frame, inputFrameReader);\n\n        final Segment mappedSegment = mapUnmanagedSegment(frameSegment);\n        final CursorFactory cursorFactory = mappedSegment.as(CursorFactory.class);\n        if (cursorFactory == null) {\n          throw new ISE(\n              \"Null cursor factory found. Probably trying to issue a query against a segment being memory unmapped.\"\n          );\n        }\n\n        if (!Intervals.ONLY_ETERNITY.equals(query.getIntervals())) {\n          // runWithInputChannel is for running on subquery results, where we don't expect to see \"intervals\" set.\n          // The SQL planner avoid it for subqueries; see DruidQuery#canUseIntervalFiltering.\n          throw DruidException.defensive(\"Expected eternity intervals, but got[%s]\", query.getIntervals());\n        }\n\n        final CursorHolder nextCursorHolder =\n            cursorFactory.makeCursorHolder(ScanQueryEngine.makeCursorBuildSpec(query, null));\n        final Cursor nextCursor = nextCursorHolder.asCursor();\n\n        if (nextCursor == null) {\n          // no cursor\n          nextCursorHolder.close();\n          return ReturnOrAwait.returnObject(Unit.instance());","sourceCodeStart":380,"sourceCodeEnd":416,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/multi-stage-query/src/main/java/org/apache/druid/msq/querykit/scan/ScanQueryFrameProcessor.java#L380-L416","documentation":"ScanQueryFrameProcessor.runWithInputChannel maps each input frame as a segment and requires a CursorFactory capability to run the scan query. A null cursor factory means the segment could not be adapted — typically because the underlying memory was unmapped while the query was running — so processing fails fast with this IllegalStateException instead of producing wrong results.","triggerScenarios":"Reading an input frame whose mapped segment no longer exposes CursorFactory, usually because the historical segment was memory-unmapped/dropped mid-query (segment cleanup, replacement, or kill during execution).","commonSituations":"Long-running MSQ scans overlapping with segment dropping/replication changes; historicals under memory pressure releasing mmaps; querying a datasource being re-ingested or overwritten concurrently.","solutions":["Re-run the query after segment activity settles; transient unmapping usually resolves it.","Pin or disable segment dropping/replacement for the duration of large scans (adjust drop/skip schedules).","Reduce query concurrency/footprint on the historical to avoid mmap eviction.","Check historical logs for segment unload events around the failure and increase segment cache capacity if evictions are frequent."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n  result = runMsqScan(scanQuery);\n} catch (IllegalStateException e) {\n  if (e.getMessage().contains('Null cursor factory')) {\n    // Transient segment unmapping; wait and retry with backoff\n    Thread.sleep(retryDelayMs);\n    result = runMsqScan(scanQuery);\n  } else throw e;\n}","preventionTips":["Avoid killing/dropping/replacing segments while large scans run.","Keep adequate segment cache capacity to limit mmap eviction.","Retry transient scan failures with backoff.","Schedule heavy scans away from segment cleanup windows."],"tags":["msq","scan","segments","memory"],"backgroundTag":"internal-invariant-violation","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}