apache/druid · error · SegmentValidationException

Unexpected end of first adapter

Error message

Unexpected end of first adapter

What it means

After the row loop, validateTwoSegments checks whether the second iterator still has rows left (it2.moveToNext() true) after the first was exhausted. If so, the second adapter has MORE rows than the first, and this SegmentValidationException ('Unexpected end of first adapter') is thrown.

Solutions

  1. Re-verify both segments' numRows (the earlier count check should have caught this; investigate if counts matched but iteration differed).
  2. Check the first segment's files for truncation/corruption and restore from deep storage if needed.
  3. Re-ingest the first segment from source data and re-validate.
  4. Debug any custom RowIterator for the first adapter for premature termination.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  io.validateTwoSegments(adapter1, adapter2);
} catch (SegmentValidationException e) {
  if (e.getMessage().contains("Unexpected end of first adapter")) {
    log.error("First adapter exhausted early — possible truncation: %s", e.getMessage());
  }
  throw e;
}

Prevention

When it happens

Trigger: it1 exhausted while it2.moveToNext() still succeeds — first adapter ended early relative to the second, indicating row-count or iteration mismatch.

Common situations: First segment truncated/corrupt so its iterator ends prematurely; custom adapter over-reporting or under-yielding rows; comparing segments from different input sets.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/segment/IndexIO.java:198

      while (it1.moveToNext()) {
        if (!it2.moveToNext()) {
          throw new SegmentValidationException("Unexpected end of second adapter");
        }
        final RowPointer rp1 = it1.getPointer();
        final RowPointer rp2 = it2.getPointer();
        ++row;
        if (rp1.getRowNum() != rp2.getRowNum()) {
          throw new SegmentValidationException("Row number mismatch: [%d] vs [%d]", rp1.getRowNum(), rp2.getRowNum());
        }
        try {
          validateRowValues(rp1, adapter1, rp2, adapter2);
        }
        catch (SegmentValidationException ex) {
          throw new SegmentValidationException(ex, "Validation failure on row %d: [%s] vs [%s]", row, rp1, rp2);
        }
      }
      if (it2.moveToNext()) {
        throw new SegmentValidationException("Unexpected end of first adapter");
      }
      if (row != adapter1.getNumRows()) {
        throw new SegmentValidationException(
            "Actual Row count mismatch. Expected [%d] found [%d]",
            row,
            adapter1.getNumRows()
        );
      }
    }
  }

  public QueryableIndex loadIndex(File inDir) throws IOException
  {
    return loadIndex(inDir, false, SegmentLazyLoadFailCallback.NOOP);
  }

  public QueryableIndex loadIndex(File inDir, boolean lazy, SegmentLazyLoadFailCallback loadFailed) throws IOException
  {

View on GitHub (pinned to 9b90983fd2)