pentaho/pentaho-kettle · error · KettleException
MultiMergeJoin.Exception.UnableToFindSpecifiedStep
MultiMergeJoin.Exception.UnableToFindSpecifiedStep
Error message
MultiMergeJoin.Exception.UnableToFindSpecifiedStep
What it means
processFirstRow() obtains the RowSet for each configured join input via findInputRowSet(inputStepName). If the runtime returns null — no row set is registered for that step name — a KettleException 'UnableToFindSpecifiedStep' is thrown before any rows can be read.
Solutions
- Check the execution log for the input steps' initialization/startup — fix whatever prevents them from starting before the join
- Verify each join input hop is enabled and the referenced step names exactly match the live steps in the transformation
- Rebuild the transformation's join configuration in Spoon (remove and re-add the input streams) to clear stale step names
- Re-run the transformation after confirming all input steps complete their init() successfully
Example fix
// before String inputStepName = streams[i].getStepname(); // "copy of Table input" mismatch rowSet = findInputRowSet( inputStepName ); // null // after // ensure config uses the actual step name present in the transformation String inputStepName = streams[i].getStepMeta().getName(); rowSet = findInputRowSet( inputStepName );
Defensive patterns
Strategy: try-catch
Validate before calling
// before execution, confirm every join input is reachable and will be running
for ( StreamInterface s : joinMeta.getInfoStreams() ) {
StepMeta sm = s.getStepMeta();
if ( sm == null || !sm.isRunning() && transMeta.findTransHop( sm, joinStep, true ) == null ) {
throw new IllegalStateException( "Join input step unavailable at runtime: " + s.getStepname() );
}
} Type guard
function rowSetExists( joinData, index ) {
return joinData.rowSets != null && index < joinData.rowSets.length && joinData.rowSets[index] != null;
} Try / catch
try {
trans.start();
trans.waitUntilFinished();
} catch ( KettleException e ) {
if ( e.getMessage() != null && e.getMessage().contains( "UnableToFindSpecifiedStep" ) ) {
// inspect log: input step likely failed init or step name mismatch
} else { throw e; }
} Prevention
- Ensure all join input steps initialize successfully — check earlier log errors
- Verify step names in the join config exactly match the live transformation steps
- Avoid conditional/optional paths that can leave join inputs unstarted
- Rebuild join configuration after cloning transformations across environments
When it happens
Trigger: Executing a transformation where the input step passed hop/metadata checks but its RowSet was never created at runtime, e.g. the input step is not actually running, its hop is disabled but skipped the earlier enabled-hop check path, or the step name in the join config doesn't match the live step instance name.
Common situations: Join configured to read from a step that executes conditionally or never starts; duplicated transformation where step instance names diverged; cluster/split execution where an input partition is absent; race where input step failed to initialize so its row set is gone.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- A deadlock was detected between steps
- All input files need to have the same number of fields. File
- AutoDoc.Exception.FilenameFieldNotFound
- AutoDoc.Exception.FileTypeFieldNotFound
- AutoDoc.Exception.UnableToDetermineLocation
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/0a1bb3a2f57f6f14.
Report an issue: GitHub.
Appendix: source
Thrown at engine/src/main/java/org/pentaho/di/trans/steps/multimerge/MultiMergeJoin.java:145
RowMetaInterface rowMeta;
data.outputRowMeta = new RowMeta();
for ( int i = 0, j = 0; i < inputStepNames.length; i++ ) {
inputStepName = inputStepNames[i];
if ( !inputStepNameList.contains( inputStepName ) ) {
//ignore step with disabled hop.
continue;
}
queueEntry = new MultiMergeJoinData.QueueEntry();
queueEntry.index = j;
data.queueEntries[j] = queueEntry;
data.results.add( new ArrayList<Object[]>() );
rowSet = findInputRowSet( inputStepName );
if ( rowSet == null ) {
throw new KettleException( BaseMessages.getString(
PKG, "MultiMergeJoin.Exception.UnableToFindSpecifiedStep", inputStepName ) );
}
data.rowSets[j] = rowSet;
row = getRowFrom( rowSet );
data.rows[j] = row;
if ( row == null ) {
rowMeta = getTransMeta().getStepFields( inputStepName );
data.metas[j] = rowMeta;
} else {
queueEntry.row = row;
rowMeta = rowSet.getRowMeta();
keyField = meta.getKeyFields()[i];
String[] keyFieldParts = keyField.split( "," );
String keyFieldPart;
data.keyNrs[j] = new int[keyFieldParts.length];
for ( int k = 0; k < keyFieldParts.length; k++ ) {View on GitHub (pinned to f3058517a1)