pentaho/pentaho-kettle · error · KettleXMLException

Unable to load job entry of type 'sql' from XML node

Error message

Unable to load job entry of type 'sql' from XML node

What it means

KettleXMLException thrown by JobEntrySQL.loadXML when parsing a 'sql' job entry's XML node fails with a KettleException. It wraps parse errors (missing/broken nodes) encountered while reading attributes like SQL filename, connection, and sendOneStatement.

Solutions

  1. Open the .kjb file in an XML validator/editor and fix malformed markup around the SQL entry node.
  2. Re-export or re-save the job from Spoon to regenerate well-formed XML.
  3. Verify the entry node has the expected SQL entry tags (sql, connection, sqlFilename, etc.).
  4. Check the wrapped cause exception for the exact parse failure.

Example fix

// before
Node entryNode = XMLHandler.getSubNode(jobNode, "entry");
sqlEntry.loadXML(entryNode, databases, slaveServers, rep, metaStore);
// after
Node entryNode = XMLHandler.getSubNode(jobNode, "entry");
if (entryNode == null) {
  throw new KettleXMLException("Missing <entry> node in job XML");
}
sqlEntry.loadXML(entryNode, databases, slaveServers, rep, metaStore);
Defensive patterns

Strategy: validation

Validate before calling

Document document = XMLHandler.loadXMLFile(jobFile);
Node job = XMLHandler.getSubNode(document, KitchenConfiguration.JOB_TAG); // or "job"
Node entry = XMLHandler.getSubNode(job, "entry");
if (entry == null) throw new KettleXMLException("Job XML has no <entry> node");

Type guard

boolean isValidEntryNode(Node n) {
  return n != null && XMLHandler.getSubNode(n, "type") != null;
}

Try / catch

try {
  sqlEntry.loadXML(entrynode, databases, slaveServers, rep, metaStore);
} catch (KettleXMLException e) {
  logError("Malformed SQL entry XML: " + e.getCause(), e);
}

Prevention

When it happens

Trigger: Calling loadXML with a .kjb <entry> node of type SQL that is malformed, missing expected tags, or whose XMLHandler parsing throws KettleXMLException/KettleException (e.g. invalid characters, truncated file).

Common situations: Hand-edited job XML with a broken <sql> entry; file truncated during transfer; XML exported from a newer PDI version with tags the older parser chokes on.

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/4dc26f168b91b985. Report an issue: GitHub.

Appendix: source

Thrown at engine/src/main/java/org/pentaho/di/job/entries/sql/JobEntrySQL.java:138

      if ( sSubs != null && sSubs.equalsIgnoreCase( "T" ) ) {
        useVariableSubstitution = true;
      }
      databaseMeta = DatabaseMeta.findDatabase( databases, dbname );

      String ssql = XMLHandler.getTagValue( entrynode, SQLFROMFILE_TAG );
      if ( ssql != null && ssql.equalsIgnoreCase( "T" ) ) {
        sqlFromFile = true;
      }

      sqlFilename = XMLHandler.getTagValue( entrynode, SQLFILENAME_TAG );

      String sOneStatement = XMLHandler.getTagValue( entrynode, SEND_ONE_STATEMENT_TAG );
      if ( sOneStatement != null && sOneStatement.equalsIgnoreCase( "T" ) ) {
        sendOneStatement = true;
      }

    } catch ( KettleException e ) {
      throw new KettleXMLException( "Unable to load job entry of type 'sql' from XML node", e );
    }
  }

  public void loadRep( Repository rep, IMetaStore metaStore, ObjectId idJobentry, List<DatabaseMeta> databases,
    List<SlaveServer> slaveServers ) throws KettleException {
    try {
      sql = rep.getJobEntryAttributeString( idJobentry, SQL_TAG );
      String sSubs = rep.getJobEntryAttributeString( idJobentry, USE_VARIABLE_SUBSTITUTION_TAG );
      if ( sSubs != null && sSubs.equalsIgnoreCase( "T" ) ) {
        useVariableSubstitution = true;
      }

      String ssql = rep.getJobEntryAttributeString( idJobentry, SQLFROMFILE_TAG );
      if ( ssql != null && ssql.equalsIgnoreCase( "T" ) ) {
        sqlFromFile = true;
      }

      String ssendOneStatement = rep.getJobEntryAttributeString( idJobentry, SEND_ONE_STATEMENT_TAG );

View on GitHub (pinned to f3058517a1)