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
- Inspect the condition text in the message and check the operand types — ensure the compared fields exist and have compatible types at runtime
- Guard against null values in the data (e.g. replace nulls with defaults before the filter step) or restructure the condition
- 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
- Avoid comparing null values directly in Conditions; coalesce first
- Verify condition fields match the incoming row schema after changing upstream steps
- Test conditions with edge-case data (nulls, mixed types) during development
- Keep PDI updated for known Condition null-comparison fixes (PDI-13227)
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
- Unable to create condition using xml:
- AddSequence.Exception.ErrorReadingSequence
- All input files need to have the same number of fields. File
- AutoDoc.Exception.FilenameFieldNotFound
- AutoDoc.Exception.FileTypeFieldNotFound
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)