apache/druid · error · SegmentValidationException

Validation failure on row

Error message

Validation failure on row %d: [%s] vs [%s]

What it means

When validateRowValues fails for a row pair inside validateTwoSegments, the caught SegmentValidationException is rethrown wrapped with row context: 'Validation failure on row %d: [%s] vs [%s]'. The original cause (e.g. dimension value or metric mismatch) is preserved as the exception cause.

Solutions

  1. Read the cause chain (getCause()) for the underlying validateRowValues error — the wrapper only locates the row.
  2. Compare the printed row pointers' values to find which column differs.
  3. Fix the converter/ingestion step that mutated the values and rebuild the segment.
  4. If the diff is benign (e.g. formatting), update the validation tolerance or the reference segment.

Example fix

// before: catching and losing context
try { io.validateTwoSegments(a, b); } catch (SegmentValidationException e) { log.error(e.getMessage()); }
// after: surface the root cause and row
try { io.validateTwoSegments(a, b); } catch (SegmentValidationException e) {
  log.error(e, "Validation failed: %s | cause: %s", e.getMessage(), e.getCause());
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  io.validateTwoSegments(adapter1, adapter2);
} catch (SegmentValidationException e) {
  Throwable cause = e.getCause();
  log.error("Row-level validation failed: %s | root cause: %s", e.getMessage(),
      cause == null ? "n/a" : cause.getMessage());
  throw e;
}

Prevention

When it happens

Trigger: Any validateRowValues failure (dimension values, metric values, timestamps differing between adapters) for the row at position %d; the wrapper identifies which row and shows both row pointers.

Common situations: Data rewritten by a converter that altered values (e.g. lossy numeric conversion, changed string encoding); rollup/dedup differences between builds; null-handling changes across Druid versions in validated segments.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

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

        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()) {
        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);

View on GitHub (pinned to 9b90983fd2)