pentaho/pentaho-kettle · error · KettleException

We cannot get step [

Error message

We cannot get step [

What it means

StepsMetrics' processRow resolves each configured step name to a StepInterface via the running transformation. When a referenced step (at the given copy number) cannot be found and that step is marked as required in the step's metadata, a KettleException is thrown. This aborts the StepsMetrics step because it cannot monitor a mandatory step that does not exist in the executing transformation.

Solutions

  1. Verify each step name in the StepsMetrics settings matches a step in the same transformation (check spelling/case).
  2. Check the configured CopyNr for each monitored step; set it to 0 for single-copy steps.
  3. Set the 'required' flag to N for steps that may be absent so the error becomes a skip instead of a throw.
  4. Re-open the transformation in Spoon and re-select the monitored steps from the dropdown so the metadata is regenerated.
  5. If transformation was exported/copied, ensure all referenced steps exist in the target version.

Example fix

// before (metadata XML references a deleted step)
<step_name>Old Step Name</step_name>
<step_copynr>0</step_copynr>
<step_required>Y</step_required>
// after (rename to existing step, or mark optional)
<step_name>New Step Name</step_name>
<step_copynr>0</step_copynr>
<step_required>N</step_required>
Defensive patterns

Strategy: validation

Validate before calling

// Before running the transformation, validate monitored steps exist
for ( int i = 0; i < stepsMetricsMeta.getStepName().length; i++ ) {
  String name = stepsMetricsMeta.getStepName()[i];
  int copyNr = Const.toInt( stepsMetricsMeta.getStepCopyNr()[i], 0 );
  if ( transMeta.findStep( name ) == null ) {
    throw new IllegalStateException( "Monitored step not found in transformation: " + name );
  }
  if ( "Y".equals( stepsMetricsMeta.getStepRequired()[i] ) && copyNr > 0
       && transMeta.findStep( name ).getCopies() <= copyNr ) {
    throw new IllegalStateException( "CopyNr " + copyNr + " does not exist for step " + name );
  }
}

Type guard

// Kotlin-style null check on the resolved StepInterface before treating it as required
val si: StepInterface? = trans.getStepInterface(stepName, copyNr)
val isAvailable: Boolean = si != null
if (!isAvailable && required) throw IllegalStateException("Missing required step $stepName copyNr=$copyNr")

Try / catch

try {
  trans.execute( null );
} catch ( KettleException e ) {
  if ( e.getMessage() != null && e.getMessage().startsWith( "We cannot get step [" ) ) {
    log.warn( "Monitored step missing; check StepsMetrics configuration", e );
  } else { throw e; }
}

Prevention

When it happens

Trigger: A step name entered in the StepsMetrics dialog does not exactly match a step in the same transformation, or the configured copy number does not exist for that step (e.g. step has only 1 copy but CopyNr=2), while the 'required' flag is set to Y.

Common situations: Renaming or deleting a monitored step after configuring StepsMetrics; copying transformations between environments where a step was removed; typo in step name; configuring copy numbers for steps that are not running multiple copies; running a transformation where the monitored step is on a disabled path that never gets instantiated.

Understand the failure class

Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.

Related errors


AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13). Data as JSON: /api/errors/c92fcdd4ae25b716. Report an issue: GitHub.

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/steps/stepsmetrics/StepsMetrics.java:107

        if ( stepnames[i].equals( getStepname() ) ) {
          throw new KettleException( "You can not get metrics for the current step [" + stepnames[i] + "]!" );
        }
        if ( targetSteps != null ) {
          // We can not metrics from the target steps
          for ( int j = 0; j < targetSteps.length; j++ ) {
            if ( stepnames[i].equals( targetSteps[j] ) ) {
              throw new KettleException( "You can not get metrics for the target step [" + targetSteps[j] + "]!" );
            }
          }
        }

        int CopyNr = Const.toInt( meta.getStepCopyNr()[i], 0 );
        StepInterface si = getTrans().getStepInterface( stepnames[i], CopyNr );
        if ( si != null ) {
          data.stepInterfaces.put( i, getDispatcher().findBaseSteps( stepnames[i] ).get( CopyNr ) );
        } else {
          if ( meta.getStepRequired()[i].equals( StepsMetricsMeta.YES ) ) {
            throw new KettleException( "We cannot get step [" + stepnames[i] + "] CopyNr=" + CopyNr + "!" );
          }
        }
      }

      data.outputRowMeta = new RowMeta();
      meta.getFields( getTransMeta().getBowl(), data.outputRowMeta, getStepname(), null, null, this, repository,
        metaStore );
    } // end if first

    data.continueLoop = true;
    // Wait until all specified steps have finished!
    while ( data.continueLoop && !isStopped() ) {
      data.continueLoop = false;
      Iterator<Entry<Integer, StepInterface>> it = data.stepInterfaces.entrySet().iterator();
      while ( it.hasNext() ) {
        Entry<Integer, StepInterface> e = it.next();
        StepInterface step = e.getValue();

View on GitHub (pinned to f3058517a1)