pentaho/pentaho-kettle · error · KettleXMLException

JobMoveFiles.Error.Exception.UnableLoadXML

Error message

JobMoveFiles.Error.Exception.UnableLoadXML

What it means

JobEntryMoveFiles.loadXML() wraps any KettleXMLException raised while parsing the entry's XML node (fields array, source_filefolder, destination_filefolder, wildcard tags) into a new KettleXMLException with localized message 'JobMoveFiles.Error.Exception.UnableLoadXML'. It signals the serialized job entry could not be read from the .kjb/.xml document.

Solutions

  1. Open the .kjb in Spoon to locate and fix the malformed Move Files entry node
  2. Restore the file from backup or source control before the corruption
  3. Compare the XML block against a known-good Move Files entry and repair tags (source_filefolder, destination_filefolder, wildcard)
  4. Check the chained KettleXMLException cause for the exact XML parse error location

Example fix

// before: <wildcard>*.csv<wildcard>  (unclosed tag)
// after
//   <fields>
//     <source_filefolder>/data/in</source_filefolder>
//     <destination_filefolder>/data/out</destination_filefolder>
//     <wildcard>.*\.csv$</wildcard>
//   </fields>
Defensive patterns

Strategy: try-catch

Validate before calling

// validate XML before loading
Document doc = XMLHandler.loadXMLFile(new File(kjbPath));
if (XMLHandler.getSubNode(XMLHandler.getSubNode(doc, "entry"), "fields") == null) {
  throw new IllegalArgumentException("Move Files entry is missing its <fields> block");
}

Try / catch

try {
  entry.loadXML(entryNode, databases, slaveServers);
} catch (KettleXMLException e) {
  Throwable cause = e.getCause();
  // cause pinpoints the XML parse error; repair the .kjb or restore from VCS
  logError("Move Files XML load failed: " + (cause != null ? cause.getMessage() : e.getMessage()), e);
  throw e;
}

Prevention

When it happens

Trigger: Loading a .kjb file whose <fields> block for the Move Files entry is malformed, missing expected tags, or produced by an incompatible writer; XMLHandler.getTagValue throws KettleXMLException on structurally invalid XML.

Common situations: Hand-edited or truncated .kjb file; file generated by a newer PDI version with a schema change; encoding corruption during transfer; merging conflicts in version-controlled .kjb files.

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/39633853f6459d3c. Report an issue: GitHub.

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/job/entries/movefiles/JobEntryMoveFiles.java:267

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

      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 );

        source_filefolder[i] = XMLHandler.getTagValue( fnode, "source_filefolder" );
        destination_filefolder[i] = XMLHandler.getTagValue( fnode, "destination_filefolder" );
        wildcard[i] = XMLHandler.getTagValue( fnode, "wildcard" );
      }
    } catch ( KettleXMLException xe ) {

      throw new KettleXMLException(
        BaseMessages.getString( PKG, "JobMoveFiles.Error.Exception.UnableLoadXML" ), xe );
    }
  }

  public void loadRep( Repository rep, IMetaStore metaStore, ObjectId id_jobentry, List<DatabaseMeta> databases,
    List<SlaveServer> slaveServers ) throws KettleException {
    try {
      move_empty_folders = rep.getJobEntryAttributeBoolean( id_jobentry, "move_empty_folders" );
      arg_from_previous = rep.getJobEntryAttributeBoolean( id_jobentry, "arg_from_previous" );
      include_subfolders = rep.getJobEntryAttributeBoolean( id_jobentry, "include_subfolders" );
      add_result_filesname = rep.getJobEntryAttributeBoolean( id_jobentry, "add_result_filesname" );
      destination_is_a_file = rep.getJobEntryAttributeBoolean( id_jobentry, "destination_is_a_file" );
      create_destination_folder = rep.getJobEntryAttributeBoolean( id_jobentry, "create_destination_folder" );
      nr_errors_less_than = rep.getJobEntryAttributeString( id_jobentry, "nr_errors_less_than" );
      success_condition = rep.getJobEntryAttributeString( id_jobentry, "success_condition" );
      add_date = rep.getJobEntryAttributeBoolean( id_jobentry, "add_date" );
      add_time = rep.getJobEntryAttributeBoolean( id_jobentry, "add_time" );
      SpecifyFormat = rep.getJobEntryAttributeBoolean( id_jobentry, "SpecifyFormat" );

View on GitHub (pinned to f3058517a1)