pentaho/pentaho-kettle · error · KettleXMLException

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

Error message

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

What it means

JobEntrySFTPPUT.loadXML catches KettleXMLException while reading the SFTP Put entry's tags from the XML node and rethrows 'Unable to load job entry of type SFTPPUT from XML node' with the original exception chained. It means the entry could not be deserialized from the .kjb XML.

Solutions

  1. Open the .kjb in an editor and validate the SFTPPUT entry XML block (tags like destinationfolder, createdestinationfolder, successWhenNoFile).
  2. Check the chained KettleXMLException (xe) for the exact parse error and line.
  3. Recreate the SFTP Put entry in Spoon and re-save the job.
  4. Restore the job file from version control or a backup if it was corrupted by editing/merging.
Defensive patterns

Strategy: validation

Validate before calling

Node entryNode = XMLHandler.getSubNode(jobnode, "entry"); if ( entryNode == null || XMLHandler.getTagValue(entryNode, "type") == null ) { throw new IllegalArgumentException("Invalid SFTPPUT entry node"); }

Try / catch

try { entry.loadXML(node, databases, slaveServers, rep, metaStore); } catch (KettleXMLException e) { log.error("SFTPPUT XML load failed: " + e.getCause(), e); }

Prevention

When it happens

Trigger: Calling loadXML when XMLHandler.getTagValue encounters malformed/missing structure in the entrynode — corrupted <entry> block, unexpected null node, or wrong node type passed to this loader.

Common situations: Truncated or hand-edited .kjb files; XML produced by incompatible Pentaho versions; copy-paste corruption; merging job files incorrectly in version control.

Related errors


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

Appendix: source

Thrown at plugins/put-file-sftp/impl/src/main/java/org/pentaho/di/job/entries/sftpput/JobEntrySFTPPUT.java:233

      proxyPort = XMLHandler.getTagValue( entrynode, "proxyPort" );
      proxyUsername = XMLHandler.getTagValue( entrynode, "proxyUsername" );
      proxyPassword =
        Encr.decryptPasswordOptionallyEncrypted( XMLHandler.getTagValue( entrynode, "proxyPassword" ) );

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

      boolean remove = "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "remove" ) );
      setAfterFTPS( getAfterSFTPPutByCode( Const.NVL( XMLHandler.getTagValue( entrynode, "aftersftpput" ), "" ) ) );
      if ( remove && getAfterFTPS() == AFTER_FTPSPUT_NOTHING ) {
        setAfterFTPS( AFTER_FTPSPUT_DELETE );
      }
      destinationfolder = XMLHandler.getTagValue( entrynode, "destinationfolder" );
      createDestinationFolder =
        "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "createdestinationfolder" ) );
      successWhenNoFile = "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "successWhenNoFile" ) );

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

  public static int getAfterSFTPPutByCode( String tt ) {
    if ( tt == null ) {
      return 0;
    }

    for ( int i = 0; i < afterFTPSCode.length; i++ ) {
      if ( afterFTPSCode[i].equalsIgnoreCase( tt ) ) {
        return i;
      }
    }
    return 0;
  }

  public static String getAfterSFTPPutDesc( int i ) {
    if ( i < 0 || i >= afterFTPSDesc.length ) {

View on GitHub (pinned to f3058517a1)