pentaho/pentaho-kettle · error · KettleValueException

The 'remainder' function only works on numeric data

Error message

The 'remainder' function only works on numeric data

What it means

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.

Solutions

  1. Convert both operands to numeric types before the Calculator step.
  2. Set field metadata to Number/Integer/BigNumber in a Select Values step.
  3. Check both metaA and metaB types in a UDJC step before computing the remainder.

Example fix

// before: fieldA % fieldB where both are String
// after: Select Values: fieldA, fieldB -> Integer; then remainder(fieldA, fieldB)
Defensive patterns

Strategy: type-guard

Validate before calling

int tA = metaA.getType();
if (tA != ValueMetaInterface.TYPE_NUMBER && tA != ValueMetaInterface.TYPE_INTEGER && tA != ValueMetaInterface.TYPE_BIGNUMBER) {
  throw new KettleValueException("remainder(A,B) requires numeric field A, got type " + tA);
}
if (metaB.getNumber(dataB).doubleValue() == 0.0) {
  throw new KettleValueException("remainder(A,B): divisor B is zero");
}

Type guard

public static boolean isNumericMeta(ValueMetaInterface m) {
  int t = m.getType();
  return t == ValueMetaInterface.TYPE_NUMBER || t == ValueMetaInterface.TYPE_INTEGER || t == ValueMetaInterface.TYPE_BIGNUMBER;
}

Try / catch

try {
  r = ValueDataUtil.remainder(metaA, dataA, metaB, dataB);
} catch (KettleValueException e) {
  logError("remainder failed: " + e.getMessage());
  r = null;
}

Prevention

When it happens

Trigger: 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".

Common situations: 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.

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


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/d13f05fe7e941be5. Report an issue: GitHub.

Appendix: source

Thrown at core/src/main/java/org/pentaho/di/core/row/ValueDataUtil.java:1417

   * @throws KettleValueException
   */
  public static Object remainder( 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() % metaB.getNumber( dataB ).doubleValue() );
      case ValueMetaInterface.TYPE_INTEGER:
        return new Long( metaA.getInteger( dataA ) % metaB.getInteger( dataB ) );
      case ValueMetaInterface.TYPE_BIGNUMBER:
        BigDecimal aValue = metaA.getBigNumber( dataA );
        BigDecimal bValue = metaA.getBigNumber( dataB );
        BigDecimal result = aValue.remainder( bValue, MathContext.UNLIMITED );
        return removeTrailingZeroFractionOrScale( result );
      default:
        throw new KettleValueException( "The 'remainder' function only works on numeric data" );
    }
  }

  public static Object nvl( ValueMetaInterface metaA, Object dataA, ValueMetaInterface metaB, Object dataB ) throws KettleValueException {
    switch ( metaA.getType() ) {
      case ValueMetaInterface.TYPE_STRING:
        if ( dataA == null ) {
          return metaB.getString( dataB );
        } else {
          return metaA.getString( dataA );
        }

      case ValueMetaInterface.TYPE_NUMBER:
        if ( dataA == null ) {
          return metaB.getNumber( dataB );
        } else {
          return metaA.getNumber( dataA );
        }

View on GitHub (pinned to f3058517a1)