pentaho/pentaho-kettle · error · KettleValueException

Calculator.Log.UnknownCalculationType + fn.getCalcType()

Error message

Calculator.Log.UnknownCalculationType + fn.getCalcType()

What it means

calcFields switches over the calculation type; if fn.getCalcType() has no matching case, the default branch throws KettleValueException with the localized 'unknown calculation type' message plus the numeric calc type. This guards against corrupted or newer metadata containing calculation codes this PDI version does not know.

Solutions

  1. Open the transformation in the same or newer PDI version that defines the calculation type
  2. Re-create the calculation using a supported type in the Calculator dialog
  3. Check for calcType values set incorrectly in code and use the CalculatorMetaFunction.CALC_* constants

Example fix

// before
fn.setCalcType(999); // unknown
// after
fn.setCalcType(CalculatorMetaFunction.CALC_ADDITION);
Defensive patterns

Strategy: try-catch

Validate before calling

int calcType = fn.getCalcType();
if (calcType < 0 || calcType > CalculatorMetaFunction.CALC_STRING_RIGHT_PAD /* max known */)
  throw new IllegalArgumentException("Unknown calc type: " + calcType);

Try / catch

try { /* calcFields */ } catch (KettleValueException e) { if (e.getMessage().contains("Unknown calculation type")) { /* open with a matching PDI version */ } throw e; }

Prevention

When it happens

Trigger: calcFields encountering a CalculatorMetaFunction whose calcType integer is outside the known CALC_* range — typically metadata written by a newer PDI version, or an invalid calcType set programmatically.

Common situations: Opening a transformation created in a newer Pentaho version in an older engine; hand-editing kettle XML and putting a bad calc_type value; custom/patched builds.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/steps/calculator/Calculator.java:611

            calcData[index] = ValueDataUtil.secondOfMinute( metaA, dataA );
            resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
            break;
          case CalculatorMetaFunction.CALC_ADD_SECONDS: // Add B seconds to date field A
            calcData[index] = ValueDataUtil.addSeconds( metaA, dataA, metaB, dataB );
            resultType = CalculatorMetaFunction.calcDefaultResultType[calcType];
            break;
          case CalculatorMetaFunction.CALC_REMAINDER:
            if ( targetMeta.getType() != metaA.getType() || targetMeta.getType() != metaB.getType() ) {
              dataA = targetMeta.convertData( metaA, dataA );
              metaA = targetMeta.clone();
              dataB = targetMeta.convertData( metaB, dataB );
              metaB = targetMeta.clone();
            }
            calcData[index] = ValueDataUtil.remainder( metaA, dataA, metaB, dataB );
            resultType = targetMeta.getType();
            break;
          default:
            throw new KettleValueException( BaseMessages.getString( PKG, "Calculator.Log.UnknownCalculationType" )
              + fn.getCalcType() );
        }

        // If we don't have a target data type, throw an error.
        // Otherwise the result is non-deterministic.
        //
        if ( targetMeta.getType() == ValueMetaInterface.TYPE_NONE ) {
          throw new KettleValueException( BaseMessages.getString( PKG, "Calculator.Log.NoType" )
            + ( i + 1 ) + " : " + fn.getFieldName() + " = " + fn.getCalcTypeDesc() + " / "
            + fn.getCalcTypeLongDesc() );
        }

        // Convert the data to the correct target data type.
        //
        if ( calcData[index] != null ) {
          if ( targetMeta.getType() != resultType ) {
            ValueMetaInterface resultMeta;
            try {

View on GitHub (pinned to f3058517a1)