{"record":{"id":"fb4c88de64fd1643","repo":"pentaho/pentaho-kettle","slug":"the-plus-function-only-works-on-numeric-data-and-strings","errorCode":null,"errorMessage":"The 'plus' function only works on numeric data and Strings.","messagePattern":"The 'plus' function only works on numeric data and Strings\\.","errorType":"exception","errorClass":"org.pentaho.di.core.exception.KettleValueException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/org/pentaho/di/core/row/ValueDataUtil.java","lineNumber":618,"sourceCode":"        } else if ( valueA == null ) {\n          return valueB;\n        } else {\n          return Boolean.valueOf( valueA.booleanValue() || valueB.booleanValue() );\n        }\n      }\n      case ValueMetaInterface.TYPE_BIGNUMBER: {\n        BigDecimal valueA = metaA.getBigNumber( dataA );\n        BigDecimal valueB = metaB.getBigNumber( dataB );\n        if ( valueB == null ) {\n          return valueA;\n        } else if ( valueA == null ) {\n          return valueB;\n        } else {\n          return valueA.add( valueB );\n        }\n      }\n      default:\n        throw new KettleValueException( \"The 'plus' function only works on numeric data and Strings.\" );\n    }\n  }\n\n  public static Object plus3( ValueMetaInterface metaA, Object dataA, ValueMetaInterface metaB, Object dataB,\n    ValueMetaInterface metaC, Object dataC ) throws KettleValueException {\n    if ( dataA == null || dataB == null || dataC == null ) {\n      return null;\n    }\n\n    switch ( metaA.getType() ) {\n      case ValueMetaInterface.TYPE_STRING:\n        return metaA.getString( dataA ) + metaB.getString( dataB ) + metaC.getString( dataC );\n      case ValueMetaInterface.TYPE_NUMBER:\n        return new Double( metaA.getNumber( dataA ).doubleValue()\n          + metaB.getNumber( dataB ).doubleValue() + metaC.getNumber( dataC ).doubleValue() );\n      case ValueMetaInterface.TYPE_INTEGER:\n        return new Long( metaA.getInteger( dataA ).longValue()\n          + metaB.getInteger( dataB ).longValue() + metaC.getInteger( dataC ).longValue() );","sourceCodeStart":600,"sourceCodeEnd":636,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/core/src/main/java/org/pentaho/di/core/row/ValueDataUtil.java#L600-L636","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nValueMetaInterface metaB = new ValueMetaBoolean(\"flag\");\nObject r = ValueDataUtil.plus(metaA, dataA, metaB, dataB); // throws\n// after\nValueMetaInterface metaB = new ValueMetaInteger(\"flagAsInt\");\nObject b = metaB.convertData(new ValueMetaBoolean(\"flag\"), dataB);\nObject r = ValueDataUtil.plus(metaA, dataA, metaB, b);","handlingStrategy":"validation","validationCode":"// Java: check both types are numeric or String before plus\nint ta = metaA.getType(), tb = metaB.getType();\nboolean ok = (ta == ValueMetaInterface.TYPE_NUMBER || ta == ValueMetaInterface.TYPE_INTEGER ||\n              ta == ValueMetaInterface.TYPE_BIGNUMBER || ta == ValueMetaInterface.TYPE_STRING)\n          && (tb == ValueMetaInterface.TYPE_NUMBER || tb == ValueMetaInterface.TYPE_INTEGER ||\n              tb == ValueMetaInterface.TYPE_BIGNUMBER || tb == ValueMetaInterface.TYPE_STRING);\nif (!ok) throw new IllegalArgumentException(\"Unsupported types for plus: \" + ta + \", \" + tb);","typeGuard":"boolean isNumericMeta(ValueMetaInterface m) {\n  int t = m.getType();\n  return t == ValueMetaInterface.TYPE_NUMBER || t == ValueMetaInterface.TYPE_INTEGER\n      || t == ValueMetaInterface.TYPE_BIGNUMBER;\n}","tryCatchPattern":"try {\n  result = ValueDataUtil.plus(metaA, dataA, metaB, dataB);\n} catch (KettleValueException e) {\n  log.error(\"plus type mismatch: A=\" + metaA.getTypeDesc() + \" B=\" + metaB.getTypeDesc());\n  throw e;\n}","preventionTips":["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"],"tags":["type-mismatch","arithmetic","kettle"],"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"}