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
- Verify the second segment's files are intact (re-fetch from deep storage, check file sizes against segment metadata).
- Re-run validateTwoSegments on a freshly downloaded copy to rule out transfer truncation.
- If the segment is corrupt, re-ingest or restore it from backup before validating.
- 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
- Verify segment file integrity after copying to/from deep storage.
- Check file sizes (IndexIO.checkFileSize) before validating segments.
- Avoid interrupting segment upload/download operations.
- Restore from replicas if a segment copy fails midway.
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
- Unexpected end of first adapter
- Bad null-marker byte, delegate class
- bucketSize must be a power of two but was[%,d]
- bucketSize must be a power of two but was[%,d]
- Cannot read frame of size [%,d] bytes
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)