pentaho/pentaho-kettle · error · KettleXMLException
Unable to load job entry of type 'FTPPUT' from XML node
Error message
Unable to load job entry of type 'FTPPUT' from XML node
What it means
JobEntryFTPPUT.loadXML wraps any KettleXMLException raised while parsing the job entry's XML node and rethrows it as 'Unable to load job entry of type FTPPUT from XML node'. This means the FTP Put job entry could not be deserialized from the .kjb XML document.
Solutions
- Open the .kjb file in a text editor and verify the FTPPUT entry XML block is well-formed with all expected tags.
- Check the chained cause (xe) in the log to identify the exact XML parse failure.
- Recreate the FTP Put job entry in Spoon and re-save the job to regenerate valid XML.
- Verify the file was not produced by an incompatible Kettle version; re-export from the original tool if needed.
Defensive patterns
Strategy: validation
Validate before calling
Document doc = XMLHandler.loadXMLFile(xmlInputStream); Node entry = XMLHandler.getSubNode(doc, "entry"); if (entry == null) throw new IllegalArgumentException("Missing <entry> node for FTPPUT"); Try / catch
try { entry.loadXML(node, databases, slaveServers, rep, metaStore); } catch (KettleXMLException e) { log.error("FTPPUT XML load failed: " + e.getCause(), e); } Prevention
- Never hand-edit .kjb files; use Spoon to modify job entries.
- Validate .kjb files in CI with a schema/round-trip load test.
- Keep source and target Pentaho versions compatible when migrating jobs.
When it happens
Trigger: Calling loadXML with a malformed or missing FTPPUT XML node — e.g. XMLHandler.getTagValue hitting an unexpected structure, a corrupted <entry> block, or an XML node for a different entry type passed to this loader.
Common situations: Hand-edited or truncated .kjb files; .kjb files from newer/older Pentaho versions with renamed tags; copy-paste corruption of job XML; loading a job saved by a different tool.
Related errors
- Unable to load job entry of type 'ftp' from XML node
- ERROR_0001_Cannot_Load_Job_Entry_From_Xml_Node
- ERROR_0001_Cannot_Load_Job_Entry_From_Xml_Node
- ERROR_0001_Cannot_Load_Job_Entry_From_Xml_Node
- ERROR_0001_Unable_To_Load_From_Xml_Node
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/267b6ed5b2450f6d.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/put-a-file-with-ftp/impl/src/main/java/org/pentaho/di/job/entries/ftpput/JobEntryFTPPUT.java:235
proxyHost = XMLHandler.getTagValue( entrynode, XML_TAG_PROXY_HOST );
proxyPort = XMLHandler.getTagValue( entrynode, XML_TAG_PROXY_PORT );
proxyUsername = XMLHandler.getTagValue( entrynode, XML_TAG_PROXY_USERNAME );
proxyPassword =
Encr.decryptPasswordOptionallyEncrypted( XMLHandler.getTagValue( entrynode, XML_TAG_PROXY_PASSWORD ) );
socksProxyHost = XMLHandler.getTagValue( entrynode, XML_TAG_SOCKSPROXY_HOST );
socksProxyPort = XMLHandler.getTagValue( entrynode, XML_TAG_SOCKSPROXY_PORT );
socksProxyUsername = XMLHandler.getTagValue( entrynode, XML_TAG_SOCKSPROXY_USERNAME );
socksProxyPassword =
Encr.decryptPasswordOptionallyEncrypted( XMLHandler.getTagValue( entrynode, XML_TAG_SOCKSPROXY_PASSWORD ) );
if ( controlEncoding == null ) {
// if we couldn't retrieve an encoding, assume it's an old instance and
// put in the the encoding used before v 2.4.0
controlEncoding = LEGACY_CONTROL_ENCODING;
}
} catch ( KettleXMLException xe ) {
throw new KettleXMLException( BaseMessages.getString( PKG, "JobFTPPUT.Log.UnableToLoadFromXml" ), xe );
}
}
@Override
public void loadRep( Repository rep, IMetaStore metaStore, ObjectId jobEntryId, List<DatabaseMeta> databases,
List<SlaveServer> slaveServers ) throws KettleException {
try {
serverName = rep.getJobEntryAttributeString( jobEntryId, XML_TAG_SERVERNAME );
serverPort = rep.getJobEntryAttributeString( jobEntryId, XML_TAG_SERVERPORT );
userName = rep.getJobEntryAttributeString( jobEntryId, XML_TAG_USERNAME );
password =
Encr.decryptPasswordOptionallyEncrypted( rep.getJobEntryAttributeString( jobEntryId, XML_TAG_PASSWORD ) );
remoteDirectory = rep.getJobEntryAttributeString( jobEntryId, XML_TAG_REMOTE_DIRECTORY );
localDirectory = rep.getJobEntryAttributeString( jobEntryId, XML_TAG_LOCAL_DIRECTORY );
wildcard = rep.getJobEntryAttributeString( jobEntryId, XML_TAG_WILDCARD );
binaryMode = rep.getJobEntryAttributeBoolean( jobEntryId, XML_TAG_BINARY );
timeout = rep.getJobEntryAttributeString( jobEntryId, XML_TAG_TIMEOUT );
remove = rep.getJobEntryAttributeBoolean( jobEntryId, XML_TAG_REMOVE );View on GitHub (pinned to f3058517a1)