pentaho/pentaho-kettle · error · KettleValueException
: Second meta data (meta2) is null, please check one of…
Error message
: Second meta data (meta2) is null, please check one of the previous steps.
What it means
ValueMetaBase.compare(Object, ValueMetaInterface, Object) requires the second value's metadata (meta2) to convert data2 before comparing. When meta2 is null it throws this KettleValueException, explicitly hinting that an upstream step produced incomplete row metadata. It is a defensive null check for broken pipeline metadata flow.
Solutions
- Check the previous steps: ensure the field's metadata exists in the RowMetaInterface before comparing (rowMeta.indexOfValue / searchValueMeta != null).
- Pass a valid non-null ValueMetaInterface for meta2 (e.g. clone or construct one matching the data).
- Add a null-guard before invoking compare and log which field/step produced missing metadata.
- Open and re-save the transformation so hop metadata is regenerated if a stale/corrupt ktr references deleted fields.
Example fix
// before
int cmp = meta1.compare(data1, rowMeta.searchValueMeta("amount"), data2); // may be null
// after
ValueMetaInterface meta2 = rowMeta.searchValueMeta("amount");
if (meta2 == null) {
throw new KettleValueException("Field 'amount' missing from previous step metadata");
}
int cmp = meta1.compare(data1, meta2, data2); Defensive patterns
Strategy: type-guard
Validate before calling
ValueMetaInterface meta2 = rowMeta.searchValueMeta(fieldName);
if (meta2 == null) {
throw new KettleValueException("Field '" + fieldName + "' missing in incoming row metadata");
}
int cmp = meta1.compare(data1, meta2, data2); Type guard
boolean canCompare(ValueMetaInterface m1, ValueMetaInterface m2) {
return m1 != null && m2 != null;
} Try / catch
try { cmp = meta1.compare(data1, meta2, data2); } catch (KettleValueException e) {
if (e.getMessage().contains("meta2) is null")) {
logger.logMinimal("Upstream step dropped field metadata; halting");
}
throw e;
} Prevention
- Always null-check searchValueMeta() results before use.
- Keep field names consistent between steps; avoid hard-coded renames.
- Re-open and re-save transformations after upstream step changes.
- Use transMeta.checkSteps() to catch missing-field references before running.
When it happens
Trigger: Calling compare(data1, null, data2) — directly or through sort/merge/stream-lookup code paths that pass a ValueMetaInterface obtained from row meta where the field lookup returned null (e.g. searchValueMeta() on a missing field name).
Common situations: A previous step in a transformation was removed/renamed so a field's metadata vanished; plugins that build RowMeta without adding all fields; passing null ValueMeta into RowSet comparison utilities; incorrect index into a row's value meta array.
Related errors
- ColumnExists.Exception.CouldnotFindField
- MappingInput.Exception.UnableToFindMappedValue
- MappingInput.Exception.UnableToFindMappedValue
- MappingInputMeta.Exception.UnknownField
- NormaliserMeta.Exception.UnableToFindField
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/24c5c605992519e9.
Report an issue: GitHub.
Appendix: source
Thrown at core/src/main/java/org/pentaho/di/core/row/value/ValueMetaBase.java:3792
}
/**
* Compare 2 values of the same data type
*
* @param data1
* the first value
* @param meta2
* the second value's metadata
* @param data2
* the second value
* @return 0 if the values are equal, -1 if data1 is smaller than data2 and +1 if it's larger.
* @throws KettleValueException
* In case we get conversion errors
*/
@Override
public int compare( Object data1, ValueMetaInterface meta2, Object data2 ) throws KettleValueException {
if ( meta2 == null ) {
throw new KettleValueException( toStringMeta()
+ " : Second meta data (meta2) is null, please check one of the previous steps." );
}
try {
// Before we can compare data1 to data2 we need to make sure they have the
// same data type etc.
//
if ( getType() == meta2.getType() ) {
if ( getStorageType() == meta2.getStorageType() ) {
return compare( data1, data2 );
}
// Convert the storage type to compare the data.
//
switch ( getStorageType() ) {
case STORAGE_TYPE_NORMAL:
return compare( data1, meta2.convertToNormalStorageType( data2 ) );
case STORAGE_TYPE_BINARY_STRING:View on GitHub (pinned to f3058517a1)