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
JobEntryFTP.loadXML() wraps any KettleXMLException raised while deserializing the classic FTP job entry's XML attributes (including nr_limit and success_condition) into a KettleXMLException with the literal message "Unable to load job entry of type 'ftp' from XML node". The original exception is chained as cause. It means the <entry type='FTP'> element in the .kjb file is unreadable by XMLHandler or contains values the loader rejects.
Solutions
- Inspect getCause() to find the failing tag and correct it in the .kjb XML.
- Reopen and re-save the FTP job entry in Spoon to regenerate valid XML.
- Validate success_condition against allowed constants (e.g., SUCCESS_IF_NO_ERRORS) if that tag is involved.
- Ensure the get-file-with-ftp plugin version matches the Pentaho version loading the job.
Example fix
<!-- before: invalid success_condition structure --> <success_condition></success_condition> <nr_limit /> <!-- after: valid defaults --> <success_condition>success_if_no_errors</success_condition> <nr_limit>10</nr_limit>
Defensive patterns
Strategy: try-catch
Validate before calling
// validate FTP entry tags before loadXML
String sc = XMLHandler.getTagValue( entrynode, "success_condition" );
if ( sc != null && !sc.equals( "success_if_no_errors" ) && !sc.equals( "success_if_errors_less" )
&& !sc.equals( "success_if_errors_less_than" ) ) {
throw new IllegalArgumentException( "Invalid success_condition in FTP entry XML: " + sc );
} Try / catch
try {
entry.loadXML( entrynode, databases, metaStore );
} catch ( KettleXMLException e ) {
log.error( "FTP entry XML load failed: " + e.getCause() );
throw new KettleException( "Fix the FTP entry XML in the .kjb file and reload", e );
} Prevention
- Avoid manual XML edits; use the Spoon job entry dialog.
- Validate job XML after migrations between Kettle versions.
- Keep the FTP plugin version aligned with the server version.
- Ensure .kjb files transfer completely (check file size/checksums).
When it happens
Trigger: Loading job XML where the FTP entry element is malformed, has unexpected tag structure from an older/newer plugin version, or fails inside the try block (getTagValue parsing, NVL handling) — thrown at the catch clause at line 345.
Common situations: Hand-edited or script-generated .kjb files; migrating jobs between Kettle/Pentaho versions where the FTP entry XML schema changed; truncated files from bad transfers; copy-paste XML errors.
Related errors
- ColumnExistsMeta.Exception.UnableToReadStepInfo
- CreditCardValidatorMeta.Exception.UnableToReadStepInfo
- CubeOutputMeta.Exception.UnableToLoadStepInfo
- DelayMeta.Exception.UnableToReadStepInfo
- DynamicSQLRowMeta.Exception.UnableToLoadStepInfo
AI-assisted analysis of pentaho/pentaho-kettle@f3058517a1 (2026-09-13).
Data as JSON: /api/errors/da75b64e8ce15d04.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/get-file-with-ftp/impl/src/main/java/org/pentaho/di/job/entries/ftp/JobEntryFTP.java:345
SifFileExists = XMLHandler.getTagValue( entrynode, "ifFileExists" );
if ( Utils.isEmpty( SifFileExists ) ) {
ifFileExists = ifFileExistsSkip;
} else {
if ( SifFileExists.equals( SifFileExistsCreateUniq ) ) {
ifFileExists = ifFileExistsCreateUniq;
} else if ( SifFileExists.equals( SifFileExistsFail ) ) {
ifFileExists = ifFileExistsFail;
} else {
ifFileExists = ifFileExistsSkip;
}
}
nr_limit = XMLHandler.getTagValue( entrynode, "nr_limit" );
success_condition =
Const.NVL( XMLHandler.getTagValue( entrynode, "success_condition" ), SUCCESS_IF_NO_ERRORS );
} 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 {
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" );
targetDirectory = rep.getJobEntryAttributeString( id_jobentry, "targetdirectory" );
wildcard = rep.getJobEntryAttributeString( id_jobentry, "wildcard" );
binaryMode = rep.getJobEntryAttributeBoolean( id_jobentry, "binary" );
timeout = rep.getJobEntryAttributeString( id_jobentry, "timeout" );
remove = rep.getJobEntryAttributeBoolean( id_jobentry, "remove" );
onlyGettingNewFiles = rep.getJobEntryAttributeBoolean( id_jobentry, "only_new" );View on GitHub (pinned to f3058517a1)