pentaho/pentaho-kettle · error · RuntimeException

Row metadata and data: unable to compare rows because of a…

Error message

Row metadata and data: unable to compare rows because of a data conversion problem

What it means

RowMetaAndData.equals() delegates row comparison to rowMeta.compare(data, other.getData()). When the two rows' metadata/values cannot be converted or compared consistently, KettleValueException is thrown and wrapped in this RuntimeException. It is thrown because equals() must not throw checked exceptions, so the conversion problem surfaces as an unchecked one.

Solutions

  1. Ensure both RowMetaAndData objects were built from the same RowMetaInterface (same fields, same order, same types)
  2. Log the KettleValueException cause to identify which field/value fails conversion
  3. Fix the value type at construction (e.g. use ValueMetaInteger for numeric data) instead of comparing mixed types
  4. Override comparison by comparing fields explicitly instead of relying on equals()
  5. Check for version skew between PDI client and repository schemas

Example fix

// before
boolean same = rowMetaAndDataA.equals(rowMetaAndDataB); // RuntimeException on type mismatch
// after
boolean same;
try {
  same = rowMetaAndDataA.equals(rowMetaAndDataB);
} catch (RuntimeException e) {
  log.logError("Row comparison failed", e.getCause());
  same = false;
}
Defensive patterns

Strategy: try-catch

Validate before calling

if (a.getRowMeta().size() != b.getRowMeta().size()) { /* rows not comparable */ }

Type guard

boolean comparable(RowMetaAndData a, RowMetaAndData b) {
  return a != null && b != null
    && a.getRowMeta().size() == b.getRowMeta().size()
    && a.getRowMeta().getFieldNames().length == b.getRowMeta().getFieldNames().length;
}

Try / catch

try {
  boolean same = rowA.equals(rowB);
} catch (RuntimeException e) {
  Throwable cause = e.getCause(); // KettleValueException with field details
  log.logError("Row compare failed: " + cause.getMessage(), cause);
}

Prevention

When it happens

Trigger: Calling equals() (directly or via collections like HashSet/List.contains/remove) on RowMetaAndData objects whose row metadata disagree in type or count with the stored data, so ValueMetaInterface comparison cannot convert one side.

Common situations: Putting RowMetaAndData objects from different repository queries into a Set; comparing rows loaded from different Pentaho/Kettle versions where a value meta type changed; rows built with mismatched addValue calls.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/37d4b39b22231252. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/pentaho/di/core/RowMetaAndData.java:117

    this.rowMeta = rowMeta;
  }

  @Override
  public int hashCode() {
    try {
      return rowMeta.hashCode( data );
    } catch ( KettleValueException e ) {
      throw new RuntimeException(
        "Row metadata and data: unable to calculate hashcode because of a data conversion problem", e );
    }
  }

  @Override
  public boolean equals( Object obj ) {
    try {
      return rowMeta.compare( data, ( (RowMetaAndData) obj ).getData() ) == 0;
    } catch ( KettleValueException e ) {
      throw new RuntimeException(
        "Row metadata and data: unable to compare rows because of a data conversion problem", e );
    }
  }

  public void addValue( ValueMetaInterface valueMeta, Object valueData ) {
    if ( valueMeta.isInteger() && ( valueData instanceof ObjectId ) ) {
      valueData = new LongObjectId( (ObjectId) valueData ).longValue();
    }
    data = RowDataUtil.addValueData( data, rowMeta.size(), valueData );
    rowMeta.addValueMeta( valueMeta );
  }

  public void addValue( String valueName, int valueType, Object valueData ) {
    ValueMetaInterface v;
    try {
      v = ValueMetaFactory.createValueMeta( valueName, valueType );
    } catch ( KettlePluginException e ) {
      v = new ValueMetaNone( valueName );

View on GitHub (pinned to f3058517a1)