{"record":{"id":"6190a11690fc7b91","repo":"apache/druid","slug":"null-cursor-factory-found-probably-trying-to-issu-6190a1","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 (ISE)","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/query/scan/ScanQueryEngine.java","lineNumber":86,"sourceCode":"      final Segment segment,\n      final ResponseContext responseContext,\n      @Nullable final QueryMetrics<?> queryMetrics\n  )\n  {\n    final Long numScannedRows = responseContext.getRowScanCount();\n    if (numScannedRows != null && numScannedRows >= query.getScanRowsLimit() && query.getTimeOrder().equals(Order.NONE)) {\n      return Sequences.empty();\n    }\n    if (segment.isTombstone()) {\n      return Sequences.empty();\n    }\n\n    final boolean hasTimeout = query.context().hasTimeout();\n    final Long timeoutAt = responseContext.getTimeoutTime();\n    final CursorFactory cursorFactory = segment.as(CursorFactory.class);\n\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    final List<String> allColumns = new ArrayList<>();\n\n    if (query.getColumns() != null && !query.getColumns().isEmpty()) {\n\n      // Unless we're in legacy mode, allColumns equals query.getColumns() exactly. This is nice since it makes\n      // the compactedList form easier to use.\n      allColumns.addAll(query.getColumns());\n    } else {\n      final Set<String> availableColumns = Sets.newLinkedHashSet(\n          Iterables.concat(\n              cursorFactory.getRowSignature().getColumnNames(),\n              Iterables.transform(\n                  Arrays.asList(query.getVirtualColumns().getVirtualColumns()),\n                  VirtualColumn::getOutputName","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/query/scan/ScanQueryEngine.java#L68-L104","documentation":"ScanQueryEngine.process obtains a CursorFactory from the segment via segment.as(CursorFactory.class). A null result means the segment no longer exposes cursors, which happens when its memory-mapped data is being unmapped (e.g. the segment was dropped or swapped out while the query ran). Druid throws ISE rather than returning partial results.","triggerScenarios":"Running a scan query against a ReferenceCountedSegment/segment whose adapter no longer supports CursorFactory because the underlying files were closed/unmapped concurrently (segment cleanup during query execution).","commonSituations":"Historical processes reloading or dropping segments while queries are in flight; segments evicted due to cache size limits; race after a segment swap during real-time hand-off.","solutions":["Retry the query; the segment swap is usually transient","Increase query concurrency handling / retry policy on the client","Avoid force-dropping segments while queries run (stagger retention tasks)","Increase historical process mmapped memory so segments are not evicted under load"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"CursorFactory cf = segment.as(CursorFactory.class);\nif (cf == null) { /* defer/requeue query */ }","typeGuard":"CursorFactory cursorFactoryOrNull(Segment s) { return s.as(CursorFactory.class); }","tryCatchPattern":"try {\n  engine.process(query, segment, ...);\n} catch (IllegalStateException e) {\n  if (e.getMessage().contains(\"Null cursor factory\")) { /* retry after short backoff */ }\n  else throw e;\n}","preventionTips":["Avoid dropping/reloading segments while queries are in flight","Enable query retry at the broker","Size segment caches to reduce eviction under load"],"tags":["druid","scan-query","segment-lifecycle","race-condition"],"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"}