{"record":{"id":"d13f05fe7e941be5","repo":"pentaho/pentaho-kettle","slug":"the-remainder-function-only-works-on-numeric-data","errorCode":null,"errorMessage":"The 'remainder' function only works on numeric data","messagePattern":"The 'remainder' 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":1417,"sourceCode":"   * @throws KettleValueException\n   */\n  public static Object remainder( 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() % metaB.getNumber( dataB ).doubleValue() );\n      case ValueMetaInterface.TYPE_INTEGER:\n        return new Long( metaA.getInteger( dataA ) % metaB.getInteger( dataB ) );\n      case ValueMetaInterface.TYPE_BIGNUMBER:\n        BigDecimal aValue = metaA.getBigNumber( dataA );\n        BigDecimal bValue = metaA.getBigNumber( dataB );\n        BigDecimal result = aValue.remainder( bValue, MathContext.UNLIMITED );\n        return removeTrailingZeroFractionOrScale( result );\n      default:\n        throw new KettleValueException( \"The 'remainder' function only works on numeric data\" );\n    }\n  }\n\n  public static Object nvl( ValueMetaInterface metaA, Object dataA, ValueMetaInterface metaB, Object dataB ) throws KettleValueException {\n    switch ( metaA.getType() ) {\n      case ValueMetaInterface.TYPE_STRING:\n        if ( dataA == null ) {\n          return metaB.getString( dataB );\n        } else {\n          return metaA.getString( dataA );\n        }\n\n      case ValueMetaInterface.TYPE_NUMBER:\n        if ( dataA == null ) {\n          return metaB.getNumber( dataB );\n        } else {\n          return metaA.getNumber( dataA );\n        }","sourceCodeStart":1399,"sourceCodeEnd":1435,"githubUrl":"https://github.com/pentaho/pentaho-kettle/blob/f3058517a153da500bf4551f46d79b91bf8ec552/core/src/main/java/org/pentaho/di/core/row/ValueDataUtil.java#L1399-L1435","documentation":"The remainder helper computes A mod B (BigDecimal.remainder with UNLIMITED precision for BigNumber) and supports only Number, Integer, and BigNumber metadata types. A non-numeric operand type falls through to the default branch and throws this KettleValueException.","triggerScenarios":"Calling ValueDataUtil.remainder(metaA, dataA, metaB, dataB) (Calculator 'A % B' / remainder) where metaA.getType() (or an operand) is not TYPE_NUMBER, TYPE_INTEGER, or TYPE_BIGNUMBER — e.g. String \"10\" % String \"3\".","commonSituations":"Modulo on string-typed IDs read from CSV; remainder of values that were re-typed to String by an upstream step; using date or boolean fields in a modulo calculation.","solutions":["Convert both operands to numeric types before the Calculator step.","Set field metadata to Number/Integer/BigNumber in a Select Values step.","Check both metaA and metaB types in a UDJC step before computing the remainder."],"exampleFix":"// before: fieldA % fieldB where both are String\n// after: Select Values: fieldA, fieldB -> Integer; then remainder(fieldA, fieldB)","handlingStrategy":"type-guard","validationCode":"int tA = metaA.getType();\nif (tA != ValueMetaInterface.TYPE_NUMBER && tA != ValueMetaInterface.TYPE_INTEGER && tA != ValueMetaInterface.TYPE_BIGNUMBER) {\n  throw new KettleValueException(\"remainder(A,B) requires numeric field A, got type \" + tA);\n}\nif (metaB.getNumber(dataB).doubleValue() == 0.0) {\n  throw new KettleValueException(\"remainder(A,B): divisor B is zero\");\n}","typeGuard":"public static boolean isNumericMeta(ValueMetaInterface m) {\n  int t = m.getType();\n  return t == ValueMetaInterface.TYPE_NUMBER || t == ValueMetaInterface.TYPE_INTEGER || t == ValueMetaInterface.TYPE_BIGNUMBER;\n}","tryCatchPattern":"try {\n  r = ValueDataUtil.remainder(metaA, dataA, metaB, dataB);\n} catch (KettleValueException e) {\n  logError(\"remainder failed: \" + e.getMessage());\n  r = null;\n}","preventionTips":["Convert both modulo operands to numeric types before the calculation.","Check for a zero divisor before computing remainder/modulo.","Pin field types with Select Values metadata steps immediately after inputs."],"tags":["kettle","pdi","calculator","numeric-types","modulo"],"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"}