pentaho/pentaho-kettle · error · RuntimeException

Unexpected error evaluation condition [

Error message

Unexpected error evaluation condition [

What it means

Condition.evaluate() wraps the whole condition evaluation (field comparisons, negation, composite sub-conditions) in a catch-all that rethrows any Exception as RuntimeException 'Unexpected error evaluation condition [<toString()>]'. It is a defensive wrapper: any failure comparing values, parsing data, or evaluating sub-conditions surfaces with the condition's textual representation for debugging.

Solutions

  1. Inspect the condition text in the message and check the operand types — ensure the compared fields exist and have compatible types at runtime
  2. Guard against null values in the data (e.g. replace nulls with defaults before the filter step) or restructure the condition
  3. Update to a PDI version with fixes for Condition null-comparison bugs (e.g. PDI-13227)

Example fix

// before
Filter rows: field = [amount], condition = '<', value = 10 // amount is sometimes null -> RuntimeException
// after
Add a 'Field exists / not null' check first, or use a coalesce step:
if (row.getInteger("amount") != null) { /* evaluate */ }
Defensive patterns

Strategy: try-catch

Validate before calling

// Before adding/evaluating a condition, verify fields exist and values are non-null of the right type
if (value == null) throw new KettleValueException("Condition field value is null: " + fieldName);

Type guard

boolean isEvaluable(Condition c, RowMetaInterface rowMeta, Object[] row) { return c.getUsedFields().length == 0 || Arrays.stream(c.getUsedFields()).allMatch(f -> rowMeta.indexOfValue(f) >= 0); }

Try / catch

try { boolean ok = condition.evaluate(rowMeta, row); } catch (RuntimeException e) { log.error("Condition evaluation failed: " + e.getMessage(), e); /* route row to error stream */ }

Prevention

When it happens

Trigger: Evaluating a Condition whose field values are of incompatible types (e.g. comparing null against a number/string in a way the comparator cannot handle), or whose field data is missing/malformed at evaluation time; also triggered in composite conditions when a sub-condition's evaluate throws.

Common situations: Transformation row filters / job entry conditions where a field is unexpectedly null or of the wrong type at runtime; conditions copied between steps whose referenced fields no longer exist; historically reported bugs like PDI-13227 (null vs number comparison).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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

Appendix: source

Thrown at core/src/main/java/org/pentaho/di/core/Condition.java:561

              break;
            case Condition.OPERATOR_AND_NOT:
              retval = retval && ( !cb.evaluate( rowMeta, r ) );
              break;
            case Condition.OPERATOR_XOR:
              retval = retval ^ cb.evaluate( rowMeta, r );
              break;
            default:
              break;
          }
        }

        // Composite: optionally negate
        if ( isNegated() ) {
          retval = !retval;
        }
      }
    } catch ( Exception e ) {
      throw new RuntimeException( "Unexpected error evaluation condition [" + toString() + "]", e );
    }

    return retval;
  }

  public void addCondition( Condition cb ) {
    if ( isAtomic() && getLeftValuename() != null ) {
      /*
       * Copy current atomic setup...
       */
      Condition current = new Condition( getLeftValuename(), getFunction(), getRightValuename(), getRightExact() );
      current.setNegated( isNegated() );
      setNegated( false );
      list.add( current );
    } else {
      // Set default operator if not on first position...
      if ( isComposite() && !list.isEmpty() && cb.getOperator() == OPERATOR_NONE ) {
        cb.setOperator( OPERATOR_AND );

View on GitHub (pinned to f3058517a1)