pentaho/pentaho-kettle · error · KettleException

SSH.Error.CommandFieldMissing

SSH.Error.CommandFieldMissing

Error message

SSH.Error.CommandFieldMissing

What it means

SSH step's processRow(), when meta.isDynamicCommand() is true, requires the name of the input field holding the command to execute. If that configured field name is empty it throws KettleException 'SSH.Error.CommandFieldMissing' before processing rows. In dynamic-command mode the step cannot know what to send over SSH without this field reference.

Solutions

  1. Open the SSH step dialog and select the input field that contains the command in the 'command field name' setting
  2. Ensure the upstream step actually produces that field and that its name matches exactly (case-sensitive)
  3. If commands are static instead, disable the dynamic-command option and type the command directly

Example fix

// before
meta.setDynamicCommand( true );
meta.setcommandfieldname( "" );
// after
meta.setDynamicCommand( true );
meta.setcommandfieldname( "cmd" ); // upstream Text File Input field named 'cmd'
Defensive patterns

Strategy: validation

Validate before calling

if ( meta.isDynamicCommand() &&
     ( meta.getcommandfieldname() == null || meta.getcommandfieldname().isEmpty() ) ) {
  throw new IllegalStateException( "SSH step: dynamic command enabled but command field name is empty" );
}

Try / catch

try {
  sshStep.processRow();
} catch ( KettleException e ) {
  if ( e.getMessage().contains( "CommandFieldMissing" ) ) {
    // set the command field in step metadata, then reinitialize
  }
}

Prevention

When it happens

Trigger: Enabling the 'execute commands from field' / dynamic-command option in the SSH step but leaving the 'command field name' setting blank; loading metadata where the commandfieldname attribute was saved empty.

Common situations: Toggling the dynamic option during testing and forgetting to pick the field; renaming a transformation copy where the field name was cleared; building the step programmatically and not calling setcommandfieldname().

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/trans/steps/ssh/SSH.java:72

    Object[] row;
    if ( meta.isDynamicCommand() ) {
      row = getRow();
      if ( row == null ) {
        setOutputDone();
        return false;
      }
      if ( first ) {
        first = false;
        data.outputRowMeta = getInputRowMeta().clone();
        data.nrInputFields = data.outputRowMeta.size();
        meta.getFields( getTransMeta().getBowl(), data.outputRowMeta, getStepname(), null, null, this, repository,
          metaStore );
        data.nrOutputFields = data.outputRowMeta.size();

        // Check if commands field is provided
        if ( meta.isDynamicCommand() ) {
          if ( Utils.isEmpty( meta.getcommandfieldname() ) ) {
            throw new KettleException( BaseMessages.getString( PKG, "SSH.Error.CommandFieldMissing" ) );
          }
          // cache the position of the source filename field
          data.indexOfCommand = data.outputRowMeta.indexOfValue( meta.getcommandfieldname() );
          if ( data.indexOfCommand < 0 ) {
            // The field is unreachable !
            throw new KettleException( BaseMessages.getString( PKG, "SSH.Exception.CouldnotFindField", meta
              .getcommandfieldname() ) );
          }
        }
      }
    } else {
      if ( !data.wroteOneRow ) {
        row = new Object[] {}; // empty row
        incrementLinesRead();
        data.wroteOneRow = true;
        if ( first ) {
          first = false;
          data.outputRowMeta = new RowMeta();

View on GitHub (pinned to f3058517a1)