pentaho/pentaho-kettle · error · KettleValueException
Subtraction can only be done with numbers!
Error message
Subtraction can only be done with numbers!
What it means
This KettleValueException is thrown by Value.minus() when subtraction is attempted on a Value whose type is not numeric (Boolean, String, or any other non-numeric VALUE_TYPE). The legacy compatibility Value class only defines arithmetic for Number, Integer, BigNumber, and Date types; anything else is rejected explicitly.
Solutions
- Convert operands to a numeric type first: value.setType( Value.VALUE_TYPE_NUMBER ) after setValue, or use convertString( VALUE_TYPE_NUMBER ).
- In transformation design, add a Select Values / Calculator step to set the field type to Integer/Number before subtraction.
- Null-check and validate input field metadata (ValueMetaInterface type) before arithmetic.
- If the string is a formatted number, parse with a NumberFormat/BigDecimal and store as BigNumber.
- Catch KettleValueException around arithmetic to fail fast with a clear field-level message.
Example fix
// before Value result = strValue.minus( other ); // throws if strValue is STRING // after Value num = new Value( "amount", strValue.getNumber() ); // or strValue.convertString( Value.VALUE_TYPE_NUMBER ) Value result = num.minus( other );
Defensive patterns
Strategy: type-guard
Validate before calling
// before subtraction
if ( !( a.isNumber() || a.isInteger() || a.isBigNumber() ) ) {
a.convertString( Value.VALUE_TYPE_NUMBER );
} Type guard
boolean isNumericValue( Value v ) {
return v != null && ( v.isNumber() || v.isInteger() || v.isBigNumber() );
} Try / catch
try {
Value result = a.minus( b );
} catch ( KettleValueException e ) {
throw new IllegalStateException( "Non-numeric operand for minus(): type=" + a.getType(), e );
} Prevention
- Force numeric field types with Select Values before arithmetic steps.
- Inspect ValueMetaInterface.getType() when building calculator logic.
- Parse text numerics with an explicit conversion step, never implicitly.
- Watch for type-inference changes after upstream refactors.
- Add metadata assertions in transformation unit tests.
When it happens
Trigger: Calling value.minus(other) (or the Kettle formula/calculator step mapping to it) where either operand's VALUE_TYPE is BOOLEAN, STRING, or an unhandled default — e.g. a field read from CSV as String then used directly in a subtraction.
Common situations: Input files where numeric columns were inferred as String; a calculator step wired to a text field; type inference changes after an upstream transformation; forgetting to use the 'Fields > Select/Type' conversion before arithmetic.
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
- Division can only be done with numeric data!
- Multiplication can only be done with numbers or a number…
- Function ABS only works with a number
- The 'divide' function only works on numeric data.
- The 'multiply' function only works on numeric data…
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/b98d3c344322c71f.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/pentaho/di/compatibility/Value.java:2197
public Value minus( byte v ) throws KettleValueException {
return minus( new Value( "tmp", (long) v ) );
}
public Value minus( Value v ) throws KettleValueException {
switch ( getType() ) {
case VALUE_TYPE_BIGNUMBER:
value.setBigNumber( getBigNumber().subtract( v.getBigNumber() ) );
break;
case VALUE_TYPE_NUMBER:
value.setNumber( getNumber() - v.getNumber() );
break;
case VALUE_TYPE_INTEGER:
value.setInteger( getInteger() - v.getInteger() );
break;
case VALUE_TYPE_BOOLEAN:
case VALUE_TYPE_STRING:
default:
throw new KettleValueException( "Subtraction can only be done with numbers!" );
}
return this;
}
public Value plus( BigDecimal v ) {
return plus( new Value( "tmp", v ) );
}
public Value plus( double v ) {
return plus( new Value( "tmp", v ) );
}
public Value plus( long v ) {
return plus( new Value( "tmp", v ) );
}
public Value plus( int v ) {
return plus( new Value( "tmp", (long) v ) );View on GitHub (pinned to f3058517a1)