pentaho/pentaho-kettle · error · KettleXMLException

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

Error message

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

What it means

JobEntryFTPDelete.loadXML parses the job entry's XML node (including encrypted socks proxy password fields) into the entry object. Any KettleXMLException raised while reading tags is rethrown with this message to identify which job entry failed to deserialize.

Solutions

  1. Open the .kjb in a text editor and validate the <entry> XML structure
  2. Recreate the FTP-delete job entry in Spoon and re-save the job
  3. Check the wrapped `xe` cause to find the exact tag that failed
  4. Upgrade/align the Pentaho version if the XML was written by a newer release

Example fix

// before
String pw = Encr.decryptPasswordOptionallyEncrypted(
  XMLHandler.getTagValue( entrynode, "socksproxy_password" ) );
// after
String pwTag = XMLHandler.getTagValue( entrynode, "socksproxy_password" );
String pw = pwTag == null ? null
  : Encr.decryptPasswordOptionallyEncrypted( pwTag );
Defensive patterns

Strategy: try-catch

Validate before calling

Document doc = XMLHandler.loadXMLFile( jobFile );
Node entry = XMLHandler.getSubNode( doc, "entry" );
if ( entry == null || XMLHandler.getTagValue( entry, "type" ) == null ) {
  throw new KettleXMLException( "Entry node missing or type tag absent" );
}

Try / catch

try { jobEntry.loadXML( entrynode, databases, metaStore ); }
catch ( KettleXMLException xe ) {
  log.error( "FTP-delete entry XML is malformed; cause=" + xe.getCause() );
  // recreate the entry rather than retrying the parse
}

Prevention

When it happens

Trigger: loadXML(entrynode) encounters malformed/missing XML structure or XMLHandler throws while reading attributes like socksproxy_password, then the catch rethrows as KettleXMLException.

Common situations: Hand-edited or truncated .kjb files; XML saved by a newer Pentaho version containing unreadable nodes; corrupted repository exports; wrong node passed to loadXML.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at plugins/ftp-delete/impl/src/main/java/org/pentaho/di/job/entries/ftpdelete/JobEntryFTPDelete.java:324

      publicpublickey = "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "publicpublickey" ) );
      keyFilename = XMLHandler.getTagValue( entrynode, "keyfilename" );
      keyFilePass = XMLHandler.getTagValue( entrynode, "keyfilepass" );

      nr_limit_success = XMLHandler.getTagValue( entrynode, "nr_limit_success" );
      success_condition = XMLHandler.getTagValue( entrynode, "success_condition" );
      copyprevious = "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "copyprevious" ) );
      FTPSConnectionType =
        FTPSConnection.getConnectionTypeByCode( Const.NVL( XMLHandler.getTagValue(
          entrynode, "ftps_connection_type" ), "" ) );
      socksProxyHost = XMLHandler.getTagValue( entrynode, "socksproxy_host" );
      socksProxyPort = XMLHandler.getTagValue( entrynode, "socksproxy_port" );
      socksProxyUsername = XMLHandler.getTagValue( entrynode, "socksproxy_username" );
      socksProxyPassword =
        Encr.decryptPasswordOptionallyEncrypted( XMLHandler.getTagValue( entrynode, "socksproxy_password" ) );

    } catch ( KettleXMLException xe ) {
      throw new KettleXMLException( "Unable to load job entry of type 'ftp' from XML node", xe );
    }
  }

  public void loadRep( Repository rep, IMetaStore metaStore, ObjectId id_jobentry, List<DatabaseMeta> databases,
    List<SlaveServer> slaveServers ) throws KettleException {
    try {
      protocol = rep.getJobEntryAttributeString( id_jobentry, "protocol" );
      port = rep.getJobEntryAttributeString( id_jobentry, "port" );
      serverName = rep.getJobEntryAttributeString( id_jobentry, "servername" );
      userName = rep.getJobEntryAttributeString( id_jobentry, "username" );
      password =
        Encr.decryptPasswordOptionallyEncrypted( rep.getJobEntryAttributeString( id_jobentry, "password" ) );
      ftpDirectory = rep.getJobEntryAttributeString( id_jobentry, "ftpdirectory" );
      wildcard = rep.getJobEntryAttributeString( id_jobentry, "wildcard" );
      timeout = rep.getJobEntryAttributeString( id_jobentry, "timeout" );
      activeConnection = rep.getJobEntryAttributeBoolean( id_jobentry, "active" );

      copyprevious = rep.getJobEntryAttributeBoolean( id_jobentry, "copyprevious" );

View on GitHub (pinned to f3058517a1)