pentaho/pentaho-kettle · error · KettleValueException
The 'A+B%' function only works on numeric data
Error message
The 'A+B%' function only works on numeric data
What it means
The 'A+B%' operation computes A + (A*B/100) and is implemented only for TYPE_NUMBER, TYPE_INTEGER, and TYPE_BIGNUMBER. A non-numeric operand falls to the default branch and throws this KettleValueException (MathContext-null path).
Solutions
- Convert the string field to Number/Integer/BigNumber before the calculation (Select values step)
- Fix the originating input step's column type mapping
- Verify the Calculator 'A+B%' field selections reference numeric columns
- Filter or default non-numeric rows before the step to avoid transformation aborts
Example fix
// before
ValueDataUtil.percent3(numMeta, dataA, strMeta, dataB); // throws
// after
ValueMetaInterface numB = new ValueMetaNumber("b");
Object b = ValueDataUtil.convertData(strMeta, dataB, numB);
ValueDataUtil.percent3(numMeta, dataA, numB, b); Defensive patterns
Strategy: type-guard
Validate before calling
if (isNumericMeta(metaA) && isNumericMeta(metaB)) {
result = /* 'A+B%' call */;
} else {
ValueMetaInterface np = new ValueMetaNumber(metaB != null ? metaB.getName() : "b");
Object p = ValueDataUtil.convertData(metaB, dataB, np);
// call with numeric metas
} Type guard
boolean isNumericMeta(ValueMetaInterface meta) {
if (meta == null) return false;
int t = meta.getType();
return t == ValueMetaInterface.TYPE_NUMBER
|| t == ValueMetaInterface.TYPE_INTEGER
|| t == ValueMetaInterface.TYPE_BIGNUMBER;
} Try / catch
try {
result = percentFn(metaA, dataA, metaB, dataB);
} catch (KettleValueException e) {
if (e.getMessage().contains("A+B%")) {
logError("Non-numeric operand for 'A+B%': " + (metaB != null ? metaB.getName() + "=" + metaB.getTypeDesc() : "?"));
}
throw e;
} Prevention
- Growth/markup percentage fields from CSV/Excel are often read as strings — set numeric types at input
- Keep percentage sign conventions consistent (0.05 vs 5) to avoid unrelated numeric bugs
- Check Calculator 'A+B%' field selections during code review of transformations
- Use error handling on the Calculator step to route bad rows instead of aborting
When it happens
Trigger: percent3-style 'A+B%' calculation without VariableSpace where either operand's value type is not numeric (String, Date, Boolean).
Common situations: Markup/growth-rate percentage fields stored as text from spreadsheets, API/JSON inputs mapped as strings, wrong field chosen in the Calculator step.
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 'A-B%' function only works on numeric data
- The 'A/B in %' function only works on numeric data
- The 'sqrt' function only works on numeric data.
- Calculator.Error.NoNameField
- Calculator.Error.UnableFindField
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/c40a27f2226da740.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/pentaho/di/core/row/ValueDataUtil.java:1089
public static Object percent3( ValueMetaInterface metaA, Object dataA, ValueMetaInterface metaB, Object dataB ) throws KettleValueException {
if ( dataA == null || dataB == null ) {
return null;
}
switch ( metaA.getType() ) {
case ValueMetaInterface.TYPE_NUMBER:
return new Double( metaA.getNumber( dataA ).doubleValue()
+ divideDoubles( multiplyDoubles( metaA.getNumber( dataA ), metaB.getNumber( dataB ) ), 100.0D ) );
case ValueMetaInterface.TYPE_INTEGER:
return new Long( metaA.getInteger( dataA ).longValue()
+ divideLongs( multiplyLongs( metaA.getInteger( dataA ), metaB.getInteger( dataB ) ), 100L ) );
case ValueMetaInterface.TYPE_BIGNUMBER:
return metaA.getBigNumber( dataA ).add(
divideBigDecimals( multiplyBigDecimals(
metaB.getBigNumber( dataB ), metaA.getBigNumber( dataA ), null ), new BigDecimal( 100 ),
(MathContext) null ) );
default:
throw new KettleValueException( "The 'A+B%' function only works on numeric data" );
}
}
public static Object percent3( ValueMetaInterface metaA, Object dataA, ValueMetaInterface metaB, Object dataB, VariableSpace space ) throws KettleValueException {
if ( dataA == null || dataB == null ) {
return null;
}
switch ( metaA.getType() ) {
case ValueMetaInterface.TYPE_NUMBER:
return new Double( metaA.getNumber( dataA ).doubleValue()
+ divideDoubles( multiplyDoubles( metaA.getNumber( dataA ), metaB.getNumber( dataB ) ), 100.0D ) );
case ValueMetaInterface.TYPE_INTEGER:
return new Long( metaA.getInteger( dataA ).longValue()
+ divideLongs( multiplyLongs( metaA.getInteger( dataA ), metaB.getInteger( dataB ) ), 100L ) );
case ValueMetaInterface.TYPE_BIGNUMBER:
return metaA.getBigNumber( dataA ).add(
divideBigDecimals( multiplyBigDecimals(View on GitHub (pinned to f3058517a1)