pentaho/pentaho-kettle · error · KettleXMLException

JobEntryDeleteFolders.UnableToLoadFromXml

Error message

JobEntryDeleteFolders.UnableToLoadFromXml

What it means

Thrown when parsing the XML definition of a 'Delete Folders' job entry fails while reading its folder list. loadXML() wraps any KettleXMLException raised by XMLHandler navigation (missing 'fields' node, malformed 'field' children) into this message. The XML is corrupt or not the expected schema version.

Solutions

  1. Validate the .kjb file is well-formed XML (xmllint) and the DeleteFolders entry has an intact <fields> section
  2. Re-export/recreate the job entry from Spoon to regenerate correct XML
  3. Check Pentaho version compatibility between the file producer and consumer
  4. Inspect the chained KettleXMLException (xe) for the exact XPath/parse failure

Example fix

// before
Node fields = XMLHandler.getSubNode(entrynode, "fields"); // NPE/malformed -> KettleXMLException
// after
Node fields = XMLHandler.getSubNode(entrynode, "fields");
if (fields == null) {
  throw new KettleXMLException("Missing <fields> node in DeleteFolders entry");
}
int nrFields = XMLHandler.countNodes(fields, "field");
Defensive patterns

Strategy: try-catch

Validate before calling

// before loadXML
Document doc = XMLHandler.loadXMLFile(new File(kjbPath)); // throws if not well-formed
Node entry = XMLHandler.getSubNode(doc.getDocumentElement(), "entry");
if (XMLHandler.getSubNode(entry, "fields") == null) {
  throw new IllegalArgumentException("DeleteFolders entry missing <fields> node");
}

Type guard

boolean hasFieldsNode(Node entrynode) {
  return entrynode != null && XMLHandler.getSubNode(entrynode, "fields") != null;
}

Try / catch

try {
  entry.loadXML(entrynode, databases, slaveServers, rep, metaStore);
} catch (KettleXMLException e) {
  logError("DeleteFolders XML invalid: " + e.getMessage(), e.getCause());
  throw e;
}

Prevention

When it happens

Trigger: Calling loadXML() (via JobMeta.fromXMLString or loading a .kjb file) where the entry node lacks a well-formed <fields> block or contains malformed <field> elements, causing XMLHandler.getTagValue/getSubNodeByNr to throw.

Common situations: Hand-edited or truncated .kjb files; XML exported by an older/newer Pentaho version with a different schema; copy-paste corruption of job metadata; wrong node passed to loadXML.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/job/entries/deletefolders/JobEntryDeleteFolders.java:152

      super.loadXML( entrynode, databases, slaveServers );
      argFromPrevious = "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "arg_from_previous" ) );
      success_condition = XMLHandler.getTagValue( entrynode, "success_condition" );
      limit_folders = XMLHandler.getTagValue( entrynode, "limit_folders" );

      Node fields = XMLHandler.getSubNode( entrynode, "fields" );

      // How many field arguments?
      int nrFields = XMLHandler.countNodes( fields, "field" );
      allocate( nrFields );

      // Read them all...
      for ( int i = 0; i < nrFields; i++ ) {
        Node fnode = XMLHandler.getSubNodeByNr( fields, "field", i );

        arguments[i] = XMLHandler.getTagValue( fnode, "name" );
      }
    } catch ( KettleXMLException xe ) {
      throw new KettleXMLException( BaseMessages.getString( PKG, "JobEntryDeleteFolders.UnableToLoadFromXml" ), xe );
    }
  }

  public void loadRep( Repository rep, IMetaStore metaStore, ObjectId id_jobentry, List<DatabaseMeta> databases,
    List<SlaveServer> slaveServers ) throws KettleException {
    try {
      argFromPrevious = rep.getJobEntryAttributeBoolean( id_jobentry, "arg_from_previous" );
      limit_folders = rep.getJobEntryAttributeString( id_jobentry, "limit_folders" );
      success_condition = rep.getJobEntryAttributeString( id_jobentry, "success_condition" );

      // How many arguments?
      int argnr = rep.countNrJobEntryAttributes( id_jobentry, "name" );
      allocate( argnr );

      // Read them all...
      for ( int a = 0; a < argnr; a++ ) {
        arguments[a] = rep.getJobEntryAttributeString( id_jobentry, a, "name" );
      }

View on GitHub (pinned to f3058517a1)