{"record":{"id":"c40a27f2226da740","repo":"pentaho/pentaho-kettle","slug":"the-a-b-function-only-works-on-numeric-data-valuedatautil","errorCode":null,"errorMessage":"The 'A+B%' function only works on numeric data","messagePattern":"The 'A\\+B%' function only works on numeric data","errorType":"exception","errorClass":"KettleValueException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/pentaho/di/core/row/ValueDataUtil.java","lineNumber":1089,"sourceCode":"  public static Object percent3( ValueMetaInterface metaA, Object dataA, ValueMetaInterface metaB, Object dataB ) throws KettleValueException {\n    if ( dataA == null || dataB == null ) {\n      return null;\n    }\n\n    switch ( metaA.getType() ) {\n      case ValueMetaInterface.TYPE_NUMBER:\n        return new Double( metaA.getNumber( dataA ).doubleValue()\n          + divideDoubles( multiplyDoubles( metaA.getNumber( dataA ), metaB.getNumber( dataB ) ), 100.0D ) );\n      case ValueMetaInterface.TYPE_INTEGER:\n        return new Long( metaA.getInteger( dataA ).longValue()\n          + divideLongs( multiplyLongs( metaA.getInteger( dataA ), metaB.getInteger( dataB ) ), 100L ) );\n      case ValueMetaInterface.TYPE_BIGNUMBER:\n        return metaA.getBigNumber( dataA ).add(\n          divideBigDecimals( multiplyBigDecimals(\n            metaB.getBigNumber( dataB ), metaA.getBigNumber( dataA ), null ), new BigDecimal( 100 ),\n            (MathContext) null ) );\n      default:\n        throw new KettleValueException( \"The 'A+B%' function only works on numeric data\" );\n    }\n  }\n\n  public static Object percent3( ValueMetaInterface metaA, Object dataA, ValueMetaInterface metaB, Object dataB, VariableSpace space ) throws KettleValueException {\n    if ( dataA == null || dataB == null ) {\n      return null;\n    }\n\n    switch ( metaA.getType() ) {\n      case ValueMetaInterface.TYPE_NUMBER:\n        return new Double( metaA.getNumber( dataA ).doubleValue()\n          + divideDoubles( multiplyDoubles( metaA.getNumber( dataA ), metaB.getNumber( dataB ) ), 100.0D ) );\n      case ValueMetaInterface.TYPE_INTEGER:\n        return new Long( metaA.getInteger( dataA ).longValue()\n          + divideLongs( multiplyLongs( metaA.getInteger( dataA ), metaB.getInteger( dataB ) ), 100L ) );\n      case ValueMetaInterface.TYPE_BIGNUMBER:\n        return metaA.getBigNumber( dataA ).add(\n          divideBigDecimals( multiplyBigDecimals(","sourceCodeStart":1071,"sourceCodeEnd":1107,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/core/src/main/java/org/pentaho/di/core/row/ValueDataUtil.java#L1071-L1107","documentation":"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).","triggerScenarios":"percent3-style 'A+B%' calculation without VariableSpace where either operand's value type is not numeric (String, Date, Boolean).","commonSituations":"Markup/growth-rate percentage fields stored as text from spreadsheets, API/JSON inputs mapped as strings, wrong field chosen in the Calculator step.","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"],"exampleFix":"// before\nValueDataUtil.percent3(numMeta, dataA, strMeta, dataB); // throws\n// after\nValueMetaInterface numB = new ValueMetaNumber(\"b\");\nObject b = ValueDataUtil.convertData(strMeta, dataB, numB);\nValueDataUtil.percent3(numMeta, dataA, numB, b);","handlingStrategy":"type-guard","validationCode":"if (isNumericMeta(metaA) && isNumericMeta(metaB)) {\n  result = /* 'A+B%' call */;\n} else {\n  ValueMetaInterface np = new ValueMetaNumber(metaB != null ? metaB.getName() : \"b\");\n  Object p = ValueDataUtil.convertData(metaB, dataB, np);\n  // call with numeric metas\n}","typeGuard":"boolean isNumericMeta(ValueMetaInterface meta) {\n  if (meta == null) return false;\n  int t = meta.getType();\n  return t == ValueMetaInterface.TYPE_NUMBER\n    || t == ValueMetaInterface.TYPE_INTEGER\n    || t == ValueMetaInterface.TYPE_BIGNUMBER;\n}","tryCatchPattern":"try {\n  result = percentFn(metaA, dataA, metaB, dataB);\n} catch (KettleValueException e) {\n  if (e.getMessage().contains(\"A+B%\")) {\n    logError(\"Non-numeric operand for 'A+B%': \" + (metaB != null ? metaB.getName() + \"=\" + metaB.getTypeDesc() : \"?\"));\n  }\n  throw e;\n}","preventionTips":["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"],"tags":["kettle","pdi","calculator","type-mismatch","percent"],"backgroundTag":"type-mismatch","analyzedSha":"f3058517a153da500bf4551f46d79b91bf8ec552","analyzedAt":"2026-09-13T14:04:16.340Z","contentChangedAt":"2026-09-13T14:04:16.340Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}