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
- Ensure both RowMetaAndData objects were built from the same RowMetaInterface (same fields, same order, same types)
- Log the KettleValueException cause to identify which field/value fails conversion
- Fix the value type at construction (e.g. use ValueMetaInteger for numeric data) instead of comparing mixed types
- Override comparison by comparing fields explicitly instead of relying on equals()
- 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
- Build compared rows from the same RowMetaInterface
- Never mix rows from different PDI versions in one collection
- Log the cause chain when equals() throws
- Prefer explicit field-by-field comparison for cross-schema rows
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
- BaseStep.SafeMode.Exception.MixingTypes
- DynamicSQLRow.Exception.TemplateReturnDataTypeError
- ElasticSearchBulk.Error.NoJsonFieldFormat
- Function MOD only works with numeric data
- Function NUM2STR only works on Numbers and Integers
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)