apache/hadoop · error · IOException

Inconsistent split cardinality from child {} ({}/{})

Error message

Inconsistent split cardinality from child {} ({}/{})

What it means

CNode.getSplits zips the ith split of every child into one CompositeInputSplit, so every source must return exactly the same number of splits; otherwise it throws IOException("Inconsistent split cardinality from child i (a/b)") with the two counts. CompositeInputFormat.getSplits defends against this by forcing job.setLong("mapred.min.split.size", Long.MAX_VALUE) so each file-based child yields a single split — hitting the error means that defense was bypassed or a child InputFormat ignores the setting.

Source

Thrown at hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-core/src/main/java/org/apache/hadoop/mapred/join/Parser.java:385

      for (Node n : kids) {
        n.setKeyComparator(cmpcl);
      }
    }

    /**
     * Combine InputSplits from child InputFormats into a
     * {@link CompositeInputSplit}.
     */
    public InputSplit[] getSplits(JobConf job, int numSplits)
        throws IOException {
      InputSplit[][] splits = new InputSplit[kids.size()][];
      for (int i = 0; i < kids.size(); ++i) {
        final InputSplit[] tmp = kids.get(i).getSplits(job, numSplits);
        if (null == tmp) {
          throw new IOException("Error gathering splits from child RReader");
        }
        if (i > 0 && splits[i-1].length != tmp.length) {
          throw new IOException("Inconsistent split cardinality from child " +
              i + " (" + splits[i-1].length + "/" + tmp.length + ")");
        }
        splits[i] = tmp;
      }
      final int size = splits[0].length;
      CompositeInputSplit[] ret = new CompositeInputSplit[size];
      for (int i = 0; i < size; ++i) {
        ret[i] = new CompositeInputSplit(splits.length);
        for (int j = 0; j < splits.length; ++j) {
          ret[i].add(splits[j][i]);
        }
      }
      return ret;
    }

    @SuppressWarnings("unchecked") // child types unknowable
    public ComposableRecordReader getRecordReader(
        InputSplit split, JobConf job, Reporter reporter) throws IOException {

View on GitHub (pinned to 2add963021)

Solutions

  1. Use CompositeInputFormat (mapred) as the job's InputFormat so its min-split-size forcing applies to all children
  2. If driving children yourself, set mapred.min.split.size to Long.MAX_VALUE in the JobConf passed to getSplits so each source yields one split
  3. Make custom child InputFormats honor mapred.min.split.size or otherwise return equal split counts
  4. Pre-partition all sources identically — same number of similarly sized, identically sorted parts

Example fix

// before: custom driver, unequal split counts
InputSplit[] a = fmtA.getSplits(job, 4); // 4 splits
InputSplit[] b = fmtB.getSplits(job, 1); // 1 split -> inconsistent cardinality

// after: one split per source, exactly what CompositeInputFormat.getSplits does
job.setLong("mapred.min.split.size", Long.MAX_VALUE);
InputSplit[] a = fmtA.getSplits(job, 1);
InputSplit[] b = fmtB.getSplits(job, 1);
Defensive patterns

Strategy: validation

Validate before calling

// preflight: force one split per source (exactly what CompositeInputFormat does)
job.setLong("mapred.min.split.size", Long.MAX_VALUE);
for (int i = 0; i < kids; i++) {
  InputSplit[] s = childFormats.get(i).getSplits(job, 1);
  if (i > 0 && s.length != prevLen) {
    throw new IOException("child " + i + " yields " + s.length
        + " splits vs " + prevLen + " from child 0");
  }
  prevLen = s.length;
}

Try / catch

try {
  return cif.getSplits(job, numSplits);
} catch (IOException e) {
  if (e.getMessage() != null && e.getMessage().contains("cardinality")) {
    throw new IOException("join sources produced unequal split counts; "
        + "set mapred.min.split.size=Long.MAX_VALUE or repartition inputs", e);
  }
  throw e;
}

Prevention

When it happens

Trigger: Driving Parser nodes directly instead of through CompositeInputFormat.getSplits; a child InputFormat (custom, or database-style) that computes splits from its own hints and ignores mapred.min.split.size; children reading inputs with very different file sizes or block counts so their split counts differ.

Common situations: Joining one large source with one small source and expecting the framework to re-shard them; custom input formats inside tbl(...); a hand-rolled driver replacing CompositeInputFormat; inputs with different replication of many small files.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/c7785fabf376a2d6. Report an issue: GitHub.