apache/druid · error · SegmentValidationException

Row count mismatch. Expected

Error message

Row count mismatch. Expected [%d] found [%d]

What it means

IndexIO.validateTwoSegments compares two segment adapters for equivalence. Before comparing rows, it first asserts both adapters report the same numRows; otherwise row-by-row comparison is meaningless. This SegmentValidationException fires when the two segments' row counts differ.

Solutions

  1. Check the two numbers in the message: determine which segment is missing/extra rows and inspect its input or conversion job logs.
  2. Re-run ingestion/conversion for the faulty segment after fixing the discrepancy.
  3. Ensure both segments were built from the same input data and time chunk before validating.
  4. If validating after a Druid upgrade, confirm row-count-affecting behavior changes (e.g. dedup, rollup settings) match between the two builds.
Defensive patterns

Strategy: validation

Validate before calling

if (adapter1.getNumRows() != adapter2.getNumRows()) {
  throw new IllegalStateException("Skip validateTwoSegments: row counts differ: "
      + adapter1.getNumRows() + " vs " + adapter2.getNumRows());
}
io.validateTwoSegments(adapter1, adapter2);

Try / catch

try {
  io.validateTwoSegments(adapter1, adapter2);
} catch (SegmentValidationException e) {
  log.error("Segment validation failed: %s", e.getMessage());
  // quarantine the segment, do not publish
}

Prevention

When it happens

Trigger: Calling IndexIO.validateTwoSegments(adapter1, adapter2) (or the File-based overload) with two segments where adapter1.getNumRows() != adapter2.getNumRows(), typically when validating a converted/rewritten segment against its source.

Common situations: Reindexing or compaction jobs that dropped or duplicated rows; comparing a shard produced by a new Druid version against the original; segment conversion tools (converter/merger) with bugs; comparing segments built from different input slices.

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/5a046baf5cb8602f. Report an issue: GitHub.

Appendix: source

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

    indexLoaders = indexLoadersBuilder.build();
  }

  public void validateTwoSegments(File dir1, File dir2) throws IOException
  {
    try (QueryableIndex queryableIndex1 = loadIndex(dir1)) {
      try (QueryableIndex queryableIndex2 = loadIndex(dir2)) {
        validateTwoSegments(
            new QueryableIndexIndexableAdapter(queryableIndex1),
            new QueryableIndexIndexableAdapter(queryableIndex2)
        );
      }
    }
  }

  public void validateTwoSegments(final IndexableAdapter adapter1, final IndexableAdapter adapter2)
  {
    if (adapter1.getNumRows() != adapter2.getNumRows()) {
      throw new SegmentValidationException(
          "Row count mismatch. Expected [%d] found [%d]",
          adapter1.getNumRows(),
          adapter2.getNumRows()
      );
    }
    {
      final Set<String> dimNames1 = Sets.newHashSet(adapter1.getDimensionNames(true));
      final Set<String> dimNames2 = Sets.newHashSet(adapter2.getDimensionNames(true));
      if (!dimNames1.equals(dimNames2)) {
        throw new SegmentValidationException(
            "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)) {

View on GitHub (pinned to 9b90983fd2)