{"record":{"id":"6b0c6668292a390e","repo":"apache/druid","slug":"trying-to-persist-an-empty-index","errorCode":null,"errorMessage":"Trying to persist an empty index!","messagePattern":"Trying to persist an empty index!","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"processing/src/main/java/org/apache/druid/segment/IndexMergerBase.java","lineNumber":135,"sourceCode":"   */\n  protected abstract void makeColumn(\n      SegmentFileBuilder segmentFileBuilder,\n      String columnName,\n      ColumnDescriptor serdeficator\n  ) throws IOException;\n\n  @Override\n  public File persist(\n      final IncrementalIndex index,\n      final Interval dataInterval,\n      File outDir,\n      IndexSpec indexSpec,\n      ProgressIndicator progress,\n      @Nullable SegmentWriteOutMediumFactory segmentWriteOutMediumFactory\n  ) throws IOException\n  {\n    if (index.isEmpty()) {\n      throw new IAE(\"Trying to persist an empty index!\");\n    }\n\n    indexSpec = indexSpec.getEffectiveSpec();\n\n    final DateTime firstTimestamp = index.getMinTime();\n    final DateTime lastTimestamp = index.getMaxTime();\n    if (!(dataInterval.contains(firstTimestamp) && dataInterval.contains(lastTimestamp))) {\n      throw new IAE(\n          \"interval[%s] does not encapsulate the full range of timestamps[%s, %s]\",\n          dataInterval,\n          firstTimestamp,\n          lastTimestamp\n      );\n    }\n\n    FileUtils.mkdirp(outDir);\n\n    log.debug(\"Starting persist for interval[%s], rows[%,d]\", dataInterval, index.numRows());","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/apache/druid/blob/9b90983fd291f26935af934383ce360473179e4d/processing/src/main/java/org/apache/druid/segment/IndexMergerBase.java#L117-L153","documentation":"IndexMergerBase.persist refuses to write an index that contains no rows. Persisting an empty incremental index would produce a meaningless segment (no timestamps, no ranges), so Druid treats it as a caller bug and throws IAE rather than emitting a broken segment.","triggerScenarios":"Calling IndexMerger.persist (or the merge/persist helpers built on it) with a deactivated incremental index that has had all rows dropped, or an index where nothing was ever added (e.g. all rows filtered out at firehose level).","commonSituations":"Ingestion tasks with filters matching zero events; stream ingestion where the window closed before any event arrived; custom code that clears an index (rollup/overwrite paths) and then persists it unconditionally.","solutions":["Guard the persist call with index.isEmpty() and skip or log instead of persisting an empty index","Fix the ingestion spec/filters so events actually reach the index","Return a null/absent segment rather than persisting when the input window is empty"],"exampleFix":"// before\nmerger.persist(index, dataInterval, outDir, indexSpec, progress, null);\n// after\nif (!index.isEmpty()) {\n  merger.persist(index, dataInterval, outDir, indexSpec, progress, null);\n} else {\n  log.warn(\"Skipping persist: index is empty for interval %s\", dataInterval);\n}","handlingStrategy":"validation","validationCode":"if (index.isEmpty()) {\n  log.warn(\"Nothing to persist; skipping segment for interval %s\", dataInterval);\n  return null;\n}","typeGuard":null,"tryCatchPattern":"try {\n  merger.persist(index, dataInterval, outDir, indexSpec, progress, null);\n} catch (IAE e) {\n  if (e.getMessage().contains(\"empty index\")) {\n    // skip: no data in this window; emit no segment\n  }\n}","preventionTips":["Check index.isEmpty() before every persist call","Review ingestion filters/window logic if windows frequently come up empty"],"tags":["druid","index-merger","empty-data","ingestion"],"backgroundTag":"empty-required-field","analyzedSha":"9b90983fd291f26935af934383ce360473179e4d","analyzedAt":"2026-09-07T13:32:30.957Z","contentChangedAt":"2026-09-07T13:32:30.957Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}