pentaho/pentaho-kettle · error · KettleXMLException

JobPGPDecryptFiles.Error.Exception.UnableLoadXML

Error message

JobPGPDecryptFiles.Error.Exception.UnableLoadXML

What it means

Thrown by JobEntryPGPDecryptFiles.loadXML when parsing the job entry's XML definition raises a KettleXMLException. The original XML parse exception is wrapped and identified with a localized message so callers know the PGP decrypt entry could not be deserialized.

Solutions

  1. Open the .kjb file in Spoon to identify the malformed entry and repair or recreate it.
  2. Validate the job XML against the expected JobEntryPGPDecryptFiles element structure (nodes with passphrase, destination_filefolder, wildcard, etc.).
  3. Restore the job file from backup or source control.
  4. Inspect getCause() (the wrapped KettleXMLException) for the exact parse error and line.

Example fix

// before: hand-edited XML with missing closing tag on <wildcard>
// after: regenerate the entry XML via Spoon, or repair:
//   <wildcard>.*\.pgp</wildcard>
// ensure every file node contains passphrase, destination_filefolder and wildcard elements
Defensive patterns

Strategy: try-catch

Validate before calling

// validate job XML before loading
Document doc = XMLHandler.loadXMLFile( jobFile );
if ( doc == null || XMLHandler.getSubNode( doc, "job" ) == null ) {
  throw new IllegalArgumentException( "Not a valid Kettle job XML" );
}

Try / catch

try {
  jobEntry.loadXML( entryNode, databases, slaveServers, rep, metaStore );
} catch ( KettleXMLException e ) {
  logError( "PGP decrypt entry XML is corrupt; restore from backup. Cause: " + e.getCause(), e );
}

Prevention

When it happens

Trigger: loadXML(entryNode, ...) reads attributes via XMLHandler.getTagValue on each node; if the XML is malformed, a required node/element is absent in a way XMLHandler rejects, or getNodes fails, KettleXMLException is caught and re-wrapped.

Common situations: Corrupt or hand-edited .kjb job file; XML exported from a newer/older Pentaho version with changed element structure; truncated file transfer; wrong node passed in by custom code.

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/47561bfc53e6806e. Report an issue: GitHub.

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/job/entries/pgpdecryptfiles/JobEntryPGPDecryptFiles.java:264

      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" );
        passphrase[i] = Encr.decryptPasswordOptionallyEncrypted( XMLHandler.getTagValue( fnode, "passphrase" ) );
        destination_filefolder[i] = XMLHandler.getTagValue( fnode, "destination_filefolder" );
        wildcard[i] = XMLHandler.getTagValue( fnode, "wildcard" );
      }
    } catch ( KettleXMLException xe ) {

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

  public void loadRep( Repository rep, IMetaStore metaStore, ObjectId id_jobentry, List<DatabaseMeta> databases,
    List<SlaveServer> slaveServers ) throws KettleException {
    try {
      gpglocation = rep.getJobEntryAttributeString( id_jobentry, "gpglocation" );
      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)