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

  1. Check the execution log for the input steps' initialization/startup — fix whatever prevents them from starting before the join
  2. Verify each join input hop is enabled and the referenced step names exactly match the live steps in the transformation
  3. Rebuild the transformation's join configuration in Spoon (remove and re-add the input streams) to clear stale step names
  4. 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

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


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)