pentaho/pentaho-kettle · error · KettleStepException
StreamLookup.Exception.UnableToFindField
Error message
StreamLookup.Exception.UnableToFindField
What it means
StreamLookup throws this while reading the lookup stream when a configured key lookup field cannot be found in the incoming lookup row metadata. indexOfValue() returns -1, meaning the field name in the step's 'key lookup' setting does not exist in the source (lookup) stream. It aborts the step with KettleStepException naming the missing field.
Solutions
- Open the StreamLookup step dialog and re-select the key lookup fields so they match fields actually present on the lookup (info/source) input stream
- Fix the upstream step so it emits the expected key field (check Rename Field/Delete/Select Values steps)
- Re-hop the correct stream to the lookup input of the StreamLookup step
- Edit the transformation XML/ktr to correct the keylookup field name typo
Example fix
// before (transformation XML) <key_lookup>cust_id</key_lookup> // after (match actual upstream field) <key_lookup>customerID</key_lookup>
Defensive patterns
Strategy: validation
Validate before calling
// Before running the transformation, validate keys exist on the lookup stream:
for (String key : meta.getKeylookup()) {
if (lookupRowMeta.indexOfValue(key) < 0)
throw new IllegalStateException("Lookup stream missing key field: " + key);
} Type guard
boolean lookupHasField(RowMetaInterface rowMeta, String field) {
return rowMeta != null && rowMeta.indexOfValue(field) >= 0;
} Try / catch
try {
step.processRow();
} catch (KettleStepException e) {
if (e.getMessage().contains("UnableToFindField")) { /* reconfigure field mapping */ }
throw e;
} Prevention
- Always click 'Get lookup fields' in the step dialog after changing upstream steps
- Add an 'Assert Fields' / 'Field exists in stream?' check before the lookup
- Review field renames in Select Values / Rename steps near the lookup
- Use version control on .ktr files so field-mapping drift is visible in diffs
When it happens
Trigger: readLookupValues (via processRow) calls rowSet.getRowMeta().indexOfValue(meta.getKeylookup()[i]) and gets -1 because a key field in the step metadata is absent from the lookup stream's row structure.
Common situations: Upstream step was renamed/deleted a field after the StreamLookup step was configured; hop wiring changed so a different stream feeds the lookup input; typo in field name in transformation XML; transformation copied between environments with different upstream logic.
Understand the failure class
Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.
Related errors
- StreamLookup.Log.FieldNotFound
- StreamLookupMeta.Exception.ReturnValueCanNotBeFound
- ColumnExists.Exception.CouldnotFindField
- CombinationLookup.Exception.FieldNotFound
- Error converting data while looking up value
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/0a11c31fcccac4a8.
Report an issue: GitHub.
Appendix: source
Thrown at engine/src/main/java/org/pentaho/di/trans/steps/streamlookup/StreamLookup.java:167
while ( rowData != null ) {
if ( log.isRowLevel() ) {
logRowlevel( BaseMessages.getString( PKG, "StreamLookup.Log.ReadLookupRow" )
+ rowSet.getRowMeta().getString( rowData ) );
}
if ( firstRun ) {
firstRun = false;
data.hasLookupRows = true;
data.infoMeta = rowSet.getRowMeta().clone();
RowMetaInterface cacheKeyMeta = new RowMeta();
RowMetaInterface cacheValueMeta = new RowMeta();
// Look up the keys in the source rows
for ( int i = 0; i < meta.getKeylookup().length; i++ ) {
keyNrs[i] = rowSet.getRowMeta().indexOfValue( meta.getKeylookup()[i] );
if ( keyNrs[i] < 0 ) {
throw new KettleStepException( BaseMessages.getString(
PKG, "StreamLookup.Exception.UnableToFindField", meta.getKeylookup()[i] ) );
}
cacheKeyMeta.addValueMeta( rowSet.getRowMeta().getValueMeta( keyNrs[i] ) );
}
// Save the data types of the keys to optionally convert input rows later on...
if ( data.keyTypes == null ) {
data.keyTypes = cacheKeyMeta.clone();
}
// Cache keys are stored as normal types, not binary
for ( int i = 0; i < keyNrs.length; i++ ) {
cacheKeyMeta.getValueMeta( i ).setStorageType( ValueMetaInterface.STORAGE_TYPE_NORMAL );
}
for ( int v = 0; v < meta.getValue().length; v++ ) {
valueNrs[v] = rowSet.getRowMeta().indexOfValue( meta.getValue()[v] );
if ( valueNrs[v] < 0 ) {
throw new KettleStepException( BaseMessages.getString(View on GitHub (pinned to f3058517a1)