apache/druid · error · SegmentValidationException

Unexpected end of second adapter

Error message

Unexpected end of second adapter

What it means

In validateTwoSegments, rows are walked in parallel via two RowIterators. If the first adapter still has rows but the second iterator is exhausted (it2.moveToNext() returns false), the second segment has fewer rows than reported/expected, and this SegmentValidationException is thrown.

Solutions

  1. Verify the second segment's files are intact (re-fetch from deep storage, check file sizes against segment metadata).
  2. Re-run validateTwoSegments on a freshly downloaded copy to rule out transfer truncation.
  3. If the segment is corrupt, re-ingest or restore it from backup before validating.
  4. If a custom adapter is involved, debug its getRows()/iterator implementation for early termination.
Defensive patterns

Strategy: try-catch

Try / catch

try {
  io.validateTwoSegments(adapter1, adapter2);
} catch (SegmentValidationException e) {
  if (e.getMessage().contains("Unexpected end of")) {
    log.error("Truncated/corrupt segment detected: %s", e.getMessage());
    // re-fetch segment from deep storage before retrying
  }
  throw e;
}

Prevention

When it happens

Trigger: Row-by-row comparison loop where it1.moveToNext() succeeds but it2.moveToNext() fails — the second adapter terminated early despite matching declared row counts, or the earlier count check was bypassed.

Common situations: Corrupt or truncated segment files (version 9+ index.zxs truncated on disk); a bug in a custom IndexableAdapter/RowIterator; deep-storage corruption after a partial copy.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

            "Dimension names differ. Expected [%s] found [%s]",
            dimNames1,
            dimNames2
        );
      }
      final Set<String> metNames1 = Sets.newHashSet(adapter1.getMetricNames());
      final Set<String> metNames2 = Sets.newHashSet(adapter2.getMetricNames());
      if (!metNames1.equals(metNames2)) {
        throw new SegmentValidationException("Metric names differ. Expected [%s] found [%s]", metNames1, metNames2);
      }
    }
    try (
        final RowIterator it1 = adapter1.getRows();
        final RowIterator it2 = adapter2.getRows()
    ) {
      long row = 0L;
      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()) {

View on GitHub (pinned to 9b90983fd2)