pentaho/pentaho-kettle · error · KettleValueException

The 'A/B in %' function only works on numeric data

Error message

The 'A/B in %' function only works on numeric data

What it means

ValueDataUtil.divideBytes... specifically the percentOfTotal-style helper computing (A/B)*100 implements only the numeric types; any non-numeric pair reaches the default branch and throws KettleValueException. It means A or B (or both) given to the 'A/B in %' operation is not a Number/Integer/BigNumber.

Solutions

  1. Convert both fields to numeric types (Select values step: String -> Number) before the Calculator step
  2. Fix the source step's column type definitions (Text/Excel input) so A and B are read as numbers
  3. Verify the Calculator field mappings for the 'A/B in %' operation reference the intended numeric fields
  4. Null-check/type-check rows before the calculation with a 'Filter rows' or 'Java Script' step

Example fix

// before
ValueMetaInterface metaA = new ValueMetaString("a");
ValueMetaInterface metaB = new ValueMetaNumber("b");
ValueDataUtil.divideByteBuffer... // 'A/B in %' call throws
// after
ValueMetaInterface metaA = new ValueMetaNumber("a");
Object aNum = ValueDataUtil.convertData(new ValueMetaString("a"), dataA, metaA);
// then call the percent function with metaA/metaB numeric
Defensive patterns

Strategy: type-guard

Validate before calling

if (isNumericMeta(metaA) && isNumericMeta(metaB)) {
  result = /* 'A/B in %' call */;
} else {
  // convert both operands to Number via ValueDataUtil.convertData before calling
}

Type guard

boolean isNumericMeta(ValueMetaInterface meta) {
  if (meta == null) return false;
  int t = meta.getType();
  return t == ValueMetaInterface.TYPE_NUMBER
    || t == ValueMetaInterface.TYPE_INTEGER
    || t == ValueMetaInterface.TYPE_BIGNUMBER;
}

Try / catch

try {
  result = percentFn(metaA, dataA, metaB, dataB);
} catch (KettleValueException e) {
  if (e.getMessage().contains("A/B in %")) {
    logError("Non-numeric operand for 'A/B in %': " + metaA.getName() + "/" + metaB.getName());
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling the 'A/B in %' calculator function (ValueDataUtil.percent... with MathContext null path) where metaA or metaB type is e.g. String, Date, or Boolean.

Common situations: String-typed columns from CSV/Excel inputs used in a Calculator percent computation, forgetting a type conversion step, field reordering in 'Select values' swapping in a string field, schema drift after upstream changes.

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/3753fc73a4fd1670. Report an issue: GitHub.

Appendix: source

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

   */
  @Deprecated
  public static Object percent1( 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 divideDoubles( multiplyDoubles( 100.0D, metaA.getNumber( dataA ) ), metaB.getNumber( dataB ) );
      case ValueMetaInterface.TYPE_INTEGER:
        return divideLongs( multiplyLongs( 100L, metaA.getInteger( dataA ) ), metaB.getInteger( dataB ) );
      case ValueMetaInterface.TYPE_BIGNUMBER:
        return divideBigDecimals(
          multiplyBigDecimals( metaA.getBigNumber( dataA ), new BigDecimal( 100 ), null ), metaB
            .getBigNumber( dataB ), (MathContext) null );

      default:
        throw new KettleValueException( "The 'A/B in %' function only works on numeric data" );
    }
  }

  public static Object percent1( ValueMetaInterface metaA, Object dataA, ValueMetaInterface metaB, Object dataB, VariableSpace space ) throws KettleValueException {
    if ( dataA == null || dataB == null ) {
      return null;
    }

    switch ( metaA.getType() ) {
      case ValueMetaInterface.TYPE_NUMBER:
        return divideDoubles( multiplyDoubles( 100.0D, metaA.getNumber( dataA ) ), metaB.getNumber( dataB ) );
      case ValueMetaInterface.TYPE_INTEGER:
        return divideLongs( multiplyLongs( 100L, metaA.getInteger( dataA ) ), metaB.getInteger( dataB ) );
      case ValueMetaInterface.TYPE_BIGNUMBER:
        return divideBigDecimals(
          multiplyBigDecimals( metaA.getBigNumber( dataA ), new BigDecimal( 100 ), null ), metaB
            .getBigNumber( dataB ), space );

View on GitHub (pinned to f3058517a1)