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
- Re-verify both segments' numRows (the earlier count check should have caught this; investigate if counts matched but iteration differed).
- Check the first segment's files for truncation/corruption and restore from deep storage if needed.
- Re-ingest the first segment from source data and re-validate.
- 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
- Check declared numRows vs iterated rows on both adapters before full validation.
- Verify segment files were fully written/copied (checksums, sizes).
- Treat interrupted segment operations as suspect and re-fetch from deep storage.
- Test custom RowIterators for premature termination.
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
- Unexpected end of second 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/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)