pentaho/pentaho-kettle · error · KettleXMLException

Unable to load job entry of type 'SFTP' from XML node

Error message

Unable to load job entry of type 'SFTP' from XML node

What it means

JobEntrySFTP.loadXML() wraps any KettleXMLException raised while reading the SFTP job entry's XML attributes (including the optional proxy settings) into a new KettleXMLException with the literal message "Unable to load job entry of type 'SFTP' from XML node". It means the <entry type='SFTP'> element in the .kjb file could not be deserialized; the original parse error is attached as cause.

Solutions

  1. Inspect getCause() to identify the exact tag that failed to parse and correct it in the .kjb file.
  2. Reopen and re-save the SFTP job entry in Spoon to regenerate valid XML.
  3. Compare the entry XML against a working SFTP entry saved by the same Kettle version and fix tag names/nesting.
  4. Confirm the get-file-sftp plugin version matches the Pentaho version loading the job.

Example fix

<!-- before: misspelled/nested tag -->
<proxyPasword>secret</proxyPasword>
<!-- after -->
<proxyPassword>secret</proxyPassword>
Defensive patterns

Strategy: try-catch

Validate before calling

// sanity-check the SFTP entry XML node before loadXML
if ( !"SFTP".equals( XMLHandler.getTagValue( entrynode, "type" ) ) && entrynode.getAttributes() == null ) {
  throw new IllegalArgumentException( "Node is not a valid SFTP job entry" );
}

Try / catch

try {
  entry.loadXML( entrynode, databases, metaStore );
} catch ( KettleXMLException e ) {
  log.error( "SFTP entry XML load failed: " + e.getCause() );
  throw new KettleException( "Correct the SFTP entry XML in the .kjb file and reload", e );
}

Prevention

When it happens

Trigger: Loading a job XML where an SFTP entry element is malformed or contains tags XMLHandler cannot parse — e.g., hand-edited XML, wrong nesting, or an attribute structure from a different plugin version — inside the try block covering getTagValue calls and password decryption.

Common situations: Manually editing .kjb files; generating job XML programmatically with wrong tag names; version migration where the SFTP entry schema changed; truncated/corrupted job files.

Related errors


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

Appendix: source

Thrown at plugins/get-file-sftp/impl/src/main/java/org/pentaho/di/job/entries/sftp/JobEntrySFTP.java:196

        isaddresult = "Y".equalsIgnoreCase( addresult );
      }

      createtargetfolder = "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "createtargetfolder" ) );
      copyprevious = "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "copyprevious" ) );

      usekeyfilename = "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "usekeyfilename" ) );
      keyfilename = XMLHandler.getTagValue( entrynode, "keyfilename" );
      keyfilepass = Encr.decryptPasswordOptionallyEncrypted( XMLHandler.getTagValue( entrynode, "keyfilepass" ) );
      compression = XMLHandler.getTagValue( entrynode, "compression" );

      proxyType = XMLHandler.getTagValue( entrynode, "proxyType" );
      proxyHost = XMLHandler.getTagValue( entrynode, "proxyHost" );
      proxyPort = XMLHandler.getTagValue( entrynode, "proxyPort" );
      proxyUsername = XMLHandler.getTagValue( entrynode, "proxyUsername" );
      proxyPassword =
        Encr.decryptPasswordOptionallyEncrypted( XMLHandler.getTagValue( entrynode, "proxyPassword" ) );
    } catch ( KettleXMLException xe ) {
      throw new KettleXMLException( "Unable to load job entry of type 'SFTP' from XML node", xe );
    }
  }

  public void loadRep( Repository rep, IMetaStore metaStore, ObjectId id_jobentry, List<DatabaseMeta> databases,
    List<SlaveServer> slaveServers ) throws KettleException {
    try {
      serverName = rep.getJobEntryAttributeString( id_jobentry, "servername" );
      serverPort = rep.getJobEntryAttributeString( id_jobentry, "serverport" );

      userName = rep.getJobEntryAttributeString( id_jobentry, "username" );
      password =
        Encr.decryptPasswordOptionallyEncrypted( rep.getJobEntryAttributeString( id_jobentry, "password" ) );

      sftpDirectory = rep.getJobEntryAttributeString( id_jobentry, "sftpdirectory" );
      targetDirectory = rep.getJobEntryAttributeString( id_jobentry, "targetdirectory" );
      wildcard = rep.getJobEntryAttributeString( id_jobentry, "wildcard" );
      remove = rep.getJobEntryAttributeBoolean( id_jobentry, "remove" );

View on GitHub (pinned to f3058517a1)