pentaho/pentaho-kettle · error · SshConnectionException

Failed to execute command

Error message

Failed to execute command: ${command}

What it means

The generic catch-all in exec converts any unexpected Exception (channel open failure, IO error while streaming output, session closed, etc.) into SshConnectionException('Failed to execute command: <command>') with the original exception attached. It means command execution failed for a reason not classified as timeout or interruption.

Solutions

  1. Check the cause attached to the exception for the true failure reason
  2. Ensure the connection is still open ( isConnected() ) before calling exec; reconnect if needed
  3. Increase server ClientAliveInterval / client heartbeat to keep long sessions alive
  4. Validate/quote the command string (avoid unescaped special characters)
  5. Reconnect and retry once on transient session-closed causes

Example fix

// before
if ( !conn.isConnected() ) { /* proceed anyway */ }
ExecResult r = conn.exec( cmd );
// after
if ( !conn.isConnected() ) { conn.connect(); conn.authenticate(); } // re-establish session
ExecResult r = conn.exec( cmd );
Defensive patterns

Strategy: try-catch

Validate before calling

// guard before exec
if ( conn == null || !conn.isConnected() ) {
  throw new IllegalStateException( "SSH session not connected; call connect() first" );
}
if ( command == null || command.trim().isEmpty() ) {
  throw new IllegalArgumentException( "Command must be non-empty" );
}

Try / catch

try { result = conn.exec( cmd ); }
catch ( SshConnectionException e ) {
  if ( e.getMessage().startsWith( "Failed to execute command" ) && e.getCause() != null ) {
    log.error( "exec of '{}' failed: {}", cmd, e.getCause().toString() );
    reconnectAndRetryOnce( conn, cmd ); // transient session-closed handling
  } else throw e;
}

Prevention

When it happens

Trigger: Any non-timeout exception inside exec: channel could not be opened, session already disconnected/closed, IOException reading stdout/stderr streams, or command string rejected by the session.

Common situations: Calling exec after the connection was closed or dropped; session timed out server-side (ClientAliveInterval) before the command ran; command too long or containing characters the shell mishandles.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/core/ssh/mina/MinaSshConnection.java:472

            Thread.sleep( 100 );
            ex = ch.getExitStatus();
          }
          exit = ex == null ? -1 : ex;
        } else {
          // Timeout occurred
          throw new SshTimeoutException( "Command execution timed out after " + timeoutMs + "ms" );
        }
      }
      String outStr = stdout.toString( StandardCharsets.UTF_8 );
      String errStr = stderr.toString( StandardCharsets.UTF_8 );
      return new ExecResult( outStr, errStr, outStr + errStr, exit, exit != 0 );
    } catch ( SshTimeoutException e ) {
      throw e; // Re-throw timeout exceptions as-is
    } catch ( InterruptedException e ) {
      Thread.currentThread().interrupt(); // Restore interrupted status
      throw new SshConnectionException( "Command execution was interrupted", e );
    } catch ( Exception e ) {
      throw new SshConnectionException( "Failed to execute command: " + command, e );
    }
  }

  @Override
  public ExecResult exec( String command ) throws SshConnectionException {
    return exec( command, config.getCommandTimeoutMillis() );
  }

  @Override
  public SftpSession openSftp() throws SshConnectionException {
    try {
      var factory = SftpClientFactory.instance();
      var sftp = factory.createSftpClient( session );
      return new MinaSftpSession( sftp );
    } catch ( Exception e ) {
      throw new SftpException( "Failed to open SFTP session", e );
    }
  }

View on GitHub (pinned to f3058517a1)