pentaho/pentaho-kettle · error · KettleXMLException

JobEntryTruncateTables.UnableLoadXML

Error message

JobEntryTruncateTables.UnableLoadXML

What it means

JobEntryTruncateTables.loadXML() wraps any KettleException raised while parsing the job entry's XML <fields> nodes into a KettleXMLException with the localized message 'JobEntryTruncateTables.UnableLoadXML'. It is thrown when the XML representation of the 'truncate tables' job entry cannot be read — typically a malformed or missing <field> sub-node. The original exception is preserved as the cause.

Solutions

  1. Open the .kjb file in a text editor and fix or remove the malformed <field> entries under the truncate-tables job entry node.
  2. Recreate the job entry in Spoon so a well-formed XML block is regenerated.
  3. Check the cause chain (KettleXMLException.getCause()) to find the exact XMLHandler failure line.
  4. Verify the file was not truncated or re-encoded (BOM/charset issues) during transfer between systems.

Example fix

// before: hand-edited .kjb with missing <name> tag
<field><schemaname>public</schemaname></field>
// after
<field><name>my_table</name><schemaname>public</schemaname></field>
Defensive patterns

Strategy: try-catch

Validate before calling

// Validate the XML node before calling loadXML
Node fields = XMLHandler.getSubNode(entrynode, "fields");
if (fields == null) throw new KettleXMLException("Missing <fields> node");
int nr = XMLHandler.countNodes(fields, "field");
for (int i = 0; i < nr; i++) {
  Node f = XMLHandler.getSubNodeByNr(fields, "field", i);
  if (f == null || XMLHandler.getTagValue(f, "name") == null)
    throw new KettleXMLException("Field " + i + " missing <name>");
}

Type guard

boolean isValidFieldsNode(Node entrynode) {
  Node fields = XMLHandler.getSubNode(entrynode, "fields");
  return fields != null && XMLHandler.countNodes(fields, "field") > 0;
}

Try / catch

try {
  jobEntry.loadXML(entrynode, databases, metaStore);
} catch ( KettleXMLException e ) {
  logError("Corrupt truncate-tables XML: " + e.getCause().getMessage(), e);
  // skip entry, load from repository, or abort the job load
}

Prevention

When it happens

Trigger: Calling loadXML(...) with an XML node whose <fields> element or its <field> children are missing/malformed, so XMLHandler.getSubNodeByNr/getTagValue throws, or the declared nrFields does not match the actual number of <field> nodes.

Common situations: Hand-edited or truncated .kjb job files; XML produced by a different Pentaho/Kettle version with changed field names; corrupted repository-export XML; copy-paste errors in the field/schemaname attributes.

Related errors


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

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/job/entries/truncatetables/JobEntryTruncateTables.java:141

      String dbname = XMLHandler.getTagValue( entrynode, "connection" );
      this.connection = DatabaseMeta.findDatabase( databases, dbname );
      this.argFromPrevious = "Y".equalsIgnoreCase( XMLHandler.getTagValue( entrynode, "arg_from_previous" ) );

      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 );
        this.arguments[i] = XMLHandler.getTagValue( fnode, "name" );
        this.schemaname[i] = XMLHandler.getTagValue( fnode, "schemaname" );
      }
    } catch ( KettleException e ) {
      throw new KettleXMLException( BaseMessages.getString( PKG, "JobEntryTruncateTables.UnableLoadXML" ), e );
    }
  }

  public void loadRep( Repository rep, IMetaStore metaStore, ObjectId id_jobentry, List<DatabaseMeta> databases,
    List<SlaveServer> slaveServers ) throws KettleException {
    try {
      connection = rep.loadDatabaseMetaFromJobEntryAttribute( id_jobentry, "connection", "id_database", databases );

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

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

View on GitHub (pinned to f3058517a1)