pentaho/pentaho-kettle · error · KettleValueException

The 'round_custom' arg C has incorrect value

Error message

The 'round_custom' arg C has incorrect value: ${valueC}

What it means

The round_custom helper takes a third argument C specifying the rounding mode, validated to be between Const.ROUND_HALF_CEILING and BigDecimal.ROUND_HALF_EVEN inclusive. If C is null or outside that range, this KettleValueException is thrown with the offending value. This guards against arbitrary integers being used as Java rounding modes.

Solutions

  1. Set field C to a valid rounding mode constant: use 1 (ROUND_HALF_CEILING/default), 2 (ROUND_HALF_DOWN), 4 (ROUND_HALF_UP), 5 (HALF_EVEN), 6 (CEILING), or the documented PDI range values.
  2. Ensure the C field is non-null and typed Integer in the Calculator step.
  3. Use a Value Mapper or Case step to normalize arbitrary mode inputs to supported constants before the calculation.

Example fix

// before: round_custom(A, B, C) with C=9
// after: round_custom(A, B, C) with C=4  // BigDecimal.ROUND_HALF_UP, inside valid range
Defensive patterns

Strategy: validation

Validate before calling

Long valueC = metaC.getInteger(dataC);
int min = Const.ROUND_HALF_CEILING;
int max = BigDecimal.ROUND_HALF_EVEN;
if (valueC == null || valueC < min || valueC > max) {
  throw new KettleValueException("rounding mode must be between " + min + " and " + max + ", got " + valueC);
}

Type guard

public static boolean isValidRoundingMode(Long mode) {
  return mode != null && mode >= Const.ROUND_HALF_CEILING && mode <= BigDecimal.ROUND_HALF_EVEN;
}

Try / catch

try {
  result = ValueDataUtil.roundCustom(metaA, dataA, metaB, dataB, metaC, dataC);
} catch (KettleValueException e) {
  logError("Invalid rounding mode in arg C: " + e.getMessage());
  result = ValueDataUtil.round(metaA, dataA, metaB, dataB); // fallback to default rounding
}

Prevention

When it happens

Trigger: Calling round_custom (Calculator 'Round custom') where field C converts to an Integer outside the valid rounding-mode constants (e.g. 5, -1, or an empty/null field), because valid modes here span only 1..7 (ROUND_HALF_CEILING..ROUND_HALF_EVEN).

Common situations: User enters a rounding mode number that is not one of the documented PDI rounding constants; field C is empty or contains a garbage value; passing java.math.RoundingMode ordinals that don't line up with the accepted legacy int constants.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


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

Appendix: source

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

   * @param metaB
   *          Metadata of decimal places
   * @param dataB
   *          decimal places
   * @param metaC
   *          Metadata of rounding mode
   * @param dataC
   *          rounding mode, e.g. java.math.BigDecimal.ROUND_HALF_EVEN
   * @return The rounded value
   * @throws KettleValueException
   */
  public static Object round( ValueMetaInterface metaA, Object dataA, ValueMetaInterface metaB, Object dataB,
      ValueMetaInterface metaC, Object dataC ) throws KettleValueException {
    if ( dataA == null || dataB == null || dataC == null ) {
      return null;
    }
    Long valueC = metaC.getInteger( dataC );
    if ( valueC == null || valueC < Const.ROUND_HALF_CEILING || valueC > BigDecimal.ROUND_HALF_EVEN ) {
      throw new KettleValueException( "The 'round_custom' arg C has incorrect value: " + valueC );
    }
    int roundingMode = valueC.intValue();
    return round( metaA, dataA, metaB, dataB, roundingMode );
  }

  public static Object ceil( ValueMetaInterface metaA, Object dataA ) throws KettleValueException {
    if ( dataA == null ) {
      return null;
    }
    switch ( metaA.getType() ) {
      case ValueMetaInterface.TYPE_NUMBER:
        return new Double( Math.ceil( metaA.getNumber( dataA ).doubleValue() ) );
      case ValueMetaInterface.TYPE_INTEGER:
        return metaA.getInteger( dataA );
      case ValueMetaInterface.TYPE_BIGNUMBER:
        return new BigDecimal( Math.ceil( metaA.getNumber( dataA ).doubleValue() ) );

      default:

View on GitHub (pinned to f3058517a1)