pentaho/pentaho-kettle · error · KettleXMLException

XPath statement returned no result [

Error message

XPath statement returned no result [

What it means

In XMLJoin.processRow(), when not using a complex join, the configured XPath statement is evaluated against the parsed target document with XPathConstants.NODE. If evaluation returns null (no node matches), a KettleXMLException 'XPath statement returned no result [<xpath>]' is thrown. The message includes the XPath statement that failed.

Solutions

  1. Print/inspect the XPath statement in the error message and verify it against the actual target XML structure.
  2. Test the XPath in an evaluator (xmllint --xpath or a browser console) against a sample target document.
  3. Handle namespaces: use local-name() predicates or configure namespace-aware evaluation if the XML is namespaced.
  4. Make the XPath tolerant (e.g. match optional elements) or validate/branch rows where the element is missing before the join.
  5. If the missing node is legitimate for some rows, restructure the transformation to filter or default those rows.

Example fix

// before: XPath assuming an unnamespaced document
/xsl:stylesheet
// after: namespace-agnostic XPath
/*[local-name()='stylesheet']
Defensive patterns

Strategy: validation

Validate before calling

// Pre-validate the XPath against a sample target document
Node n = (Node) xpath.evaluate(xpathStmt, sampleTargetDom, XPathConstants.NODE);
if (n == null) throw new IllegalArgumentException("XPath matches nothing: " + xpathStmt);

Try / catch

try {
  xmlJoinStep.processRow();
} catch (KettleXMLException e) {
  if (e.getMessage().startsWith("XPath statement returned no result")) {
    log.error("XPath '{}' matched no node; verify structure/namespaces", e);
  }
}

Prevention

When it happens

Trigger: Simple join mode where the XPath statement stored in the step settings matches no node in the target XML document for a given row — wrong path, wrong namespace context, or the target XML lacks the expected element.

Common situations: Typo in the XPath expression; XML uses namespaces so element names need a namespace-aware expression; target XML structure changed upstream; XPath references an element that only sometimes exists.

Understand the failure class

Background: EmptyResultError / "no results found": when an API or scraper succeeds but returns zero rows — this error's family across 9 libraries.

Related errors


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

Appendix: source

Thrown at plugins/xml/core/src/main/java/org/pentaho/di/trans/steps/xmljoin/XMLJoin.java:124

      data.outputRowMeta = data.TargetRowSet.getRowMeta().clone();
      meta.getFields( getTransMeta().getBowl(), data.outputRowMeta, getStepname(),
          new RowMetaInterface[] { data.TargetRowSet.getRowMeta() }, null, getTransMeta(), repository, metaStore );
      data.outputRowData = rTarget.clone();

      // get the target xml structure and create a DOM
      String strTarget = (String) rTarget[target_field_id];
      // parse the XML as a W3C Document

      InputSource inputSource = new InputSource( new StringReader( strTarget ) );

      data.XPathStatement = meta.getTargetXPath();
      try {
        DocumentBuilder builder = XMLParserFactoryProducer.createSecureDocBuilderFactory().newDocumentBuilder();
        data.targetDOM = builder.parse( inputSource );
        if ( !meta.isComplexJoin() ) {
          data.targetNode = (Node) xpath.evaluate( data.XPathStatement, data.targetDOM, XPathConstants.NODE );
          if ( data.targetNode == null ) {
            throw new KettleXMLException( "XPath statement returned no result [" + data.XPathStatement + "]" );
          }
        }
      } catch ( Exception e ) {
        throw new KettleXMLException( e );
      }

    }

    Object[] rJoinSource = getRowFrom( data.SourceRowSet ); // This also waits for a row to be finished.
    if ( rJoinSource == null ) {
      // no more input to be expected... create the output row
      try {
        if ( meta.isOmitNullValues() ) {
          removeEmptyNodes( data.targetDOM.getChildNodes() );
        }
        // create string from xml tree
        StringWriter sw = new StringWriter();
        StreamResult resultXML = new StreamResult( sw );

View on GitHub (pinned to f3058517a1)