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
- Print/inspect the XPath statement in the error message and verify it against the actual target XML structure.
- Test the XPath in an evaluator (xmllint --xpath or a browser console) against a sample target document.
- Handle namespaces: use local-name() predicates or configure namespace-aware evaluation if the XML is namespaced.
- Make the XPath tolerant (e.g. match optional elements) or validate/branch rows where the element is missing before the join.
- 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
- Test XPaths against real sample XML before configuring the step.
- Use local-name() predicates for namespaced XML.
- Keep target XML structure stable or update the XPath when upstream changes.
- Handle optional elements with a default/fallback branch upstream.
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
- GetXMLData.Log.UnableApplyXPath (localized message)
- GetXMLDateLoopNodesImportProgressDialog.Exception.ErrorScann…
- XMLJoin.Exception.FieldNotFound
- A connection of type PALO is expected
- A connection of type PALO is expected
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)