pentaho/pentaho-kettle · error · org.pentaho.di.core.exception.KettleValueException
The 'plus' function only works on numeric data and Strings.
Error message
The 'plus' function only works on numeric data and Strings.
What it means
ValueDataUtil.plus adds two values but only supports numeric types (Number, BigNumber, Date semantics) and String concatenation. If either ValueMeta's type falls outside the switch's supported cases, it throws KettleValueException. It is a type-compatibility guard for the Calculator/UDJE '+' operation.
Solutions
- Use a Select values / Calculator step to convert both fields to Number/Integer/String before '+'
- Fix the incoming field type metadata so it is numeric or String
- Check that no upstream step redefined the field type (e.g. Boolean)
- If concatenation was intended, ensure both metas are TYPE_STRING
Example fix
// before
ValueMetaInterface metaB = new ValueMetaBoolean("flag");
Object r = ValueDataUtil.plus(metaA, dataA, metaB, dataB); // throws
// after
ValueMetaInterface metaB = new ValueMetaInteger("flagAsInt");
Object b = metaB.convertData(new ValueMetaBoolean("flag"), dataB);
Object r = ValueDataUtil.plus(metaA, dataA, metaB, b); Defensive patterns
Strategy: validation
Validate before calling
// Java: check both types are numeric or String before plus
int ta = metaA.getType(), tb = metaB.getType();
boolean ok = (ta == ValueMetaInterface.TYPE_NUMBER || ta == ValueMetaInterface.TYPE_INTEGER ||
ta == ValueMetaInterface.TYPE_BIGNUMBER || ta == ValueMetaInterface.TYPE_STRING)
&& (tb == ValueMetaInterface.TYPE_NUMBER || tb == ValueMetaInterface.TYPE_INTEGER ||
tb == ValueMetaInterface.TYPE_BIGNUMBER || tb == ValueMetaInterface.TYPE_STRING);
if (!ok) throw new IllegalArgumentException("Unsupported types for plus: " + ta + ", " + tb); Type guard
boolean isNumericMeta(ValueMetaInterface m) {
int t = m.getType();
return t == ValueMetaInterface.TYPE_NUMBER || t == ValueMetaInterface.TYPE_INTEGER
|| t == ValueMetaInterface.TYPE_BIGNUMBER;
} Try / catch
try {
result = ValueDataUtil.plus(metaA, dataA, metaB, dataB);
} catch (KettleValueException e) {
log.error("plus type mismatch: A=" + metaA.getTypeDesc() + " B=" + metaB.getTypeDesc());
throw e;
} Prevention
- Explicitly type incoming fields (CSV/text input) instead of relying on defaults
- Insert a Select values (metadata) step to normalize types before arithmetic
- Review upstream steps after type changes to downstream calculators
- Prefer converting Booleans/Binary to numeric explicitly rather than letting '+' fail
When it happens
Trigger: Calling ValueDataUtil.plus(metaA, dataA, metaB, dataB) where either value's meta type is not TYPE_NUMBER, TYPE_INTEGER, TYPE_BIGNUMBER, TYPE_STRING, or the other supported cases — e.g. Boolean, Binary, or an unknown/unspecified type.
Common situations: Calculator step '+' fed a Boolean or Binary field; incoming CSV field defaulted to 'Unknown' type; upstream step changed a field from Number to String/Boolean after a transform edit.
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
- The 'divide' function only works on numeric data.
- The 'multiply' function only works on numeric data…
- BaseStep.SafeMode.Exception.MixingTypes
- Division can only be done with numeric data!
- DynamicSQLRow.Exception.TemplateReturnDataTypeError
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/fb4c88de64fd1643.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/pentaho/di/core/row/ValueDataUtil.java:618
} else if ( valueA == null ) {
return valueB;
} else {
return Boolean.valueOf( valueA.booleanValue() || valueB.booleanValue() );
}
}
case ValueMetaInterface.TYPE_BIGNUMBER: {
BigDecimal valueA = metaA.getBigNumber( dataA );
BigDecimal valueB = metaB.getBigNumber( dataB );
if ( valueB == null ) {
return valueA;
} else if ( valueA == null ) {
return valueB;
} else {
return valueA.add( valueB );
}
}
default:
throw new KettleValueException( "The 'plus' function only works on numeric data and Strings." );
}
}
public static Object plus3( ValueMetaInterface metaA, Object dataA, ValueMetaInterface metaB, Object dataB,
ValueMetaInterface metaC, Object dataC ) throws KettleValueException {
if ( dataA == null || dataB == null || dataC == null ) {
return null;
}
switch ( metaA.getType() ) {
case ValueMetaInterface.TYPE_STRING:
return metaA.getString( dataA ) + metaB.getString( dataB ) + metaC.getString( dataC );
case ValueMetaInterface.TYPE_NUMBER:
return new Double( metaA.getNumber( dataA ).doubleValue()
+ metaB.getNumber( dataB ).doubleValue() + metaC.getNumber( dataC ).doubleValue() );
case ValueMetaInterface.TYPE_INTEGER:
return new Long( metaA.getInteger( dataA ).longValue()
+ metaB.getInteger( dataB ).longValue() + metaC.getInteger( dataC ).longValue() );View on GitHub (pinned to f3058517a1)