jhy/jsoup · error · Selector.SelectorParseException
Could not evaluate XPath query
Error message
Could not evaluate XPath query [%s]: %s
What it means
W3CDom.selectXpath compiles and evaluates an XPath expression against a W3C DOM context node. If the XPath engine throws XPathExpressionException or XPathFactoryConfigurationException, jsoup wraps it in a Selector.SelectorParseException with the query and engine message. It indicates the XPath was syntactically invalid, used unsupported functions/axes, or the XPath factory was misconfigured.
Solutions
- Check the engine message after the colon for the exact syntax error at the reported offset.
- Test the query in a standalone XPath evaluator; fix syntax (e.g. use @attr, correct predicates).
- Remove or fix javax.xml.xpath.XPathFactory system properties that install a broken custom factory.
- Simplify to XPath 1.0-compatible expressions if using unsupported functions.
- Catch Selector.SelectorParseException for user-supplied queries and surface a friendly error.
Example fix
// before
W3CDom.selectXpath("//div[@class=\"x\"]", doc); // wrong quoting
// after
W3CDom.selectXpath("//div[@class='x']", doc); Defensive patterns
Strategy: try-catch
Validate before calling
// basic XPath sanity check before calling
if (xpath == null || xpath.isBlank() || xpath.contains("[") != xpath.contains("]")) throw new IllegalArgumentException("malformed xpath"); Try / catch
try { W3CDom.selectXpath(xpath, w3cDoc); } catch (org.jsoup.select.Selector.SelectorParseException e) { log.error("Bad XPath '{}': {}", xpath, e.getMessage()); } Prevention
- Validate user-supplied XPath against XPath 1.0 grammar (JDK default engine).
- Avoid overriding javax.xml.xpath.XPathFactory system properties unless necessary.
- Unit-test XPath queries against a fixed sample document.
- Escape quotes consistently: single quotes inside double-quoted attribute predicates.
When it happens
Trigger: Passing a malformed XPath string (bad syntax, unknown function, invalid axis) to W3CDom.selectXpath/Jsoup.selectXpath; using an XPath feature unsupported by the JDK's default factory; a system property like javax.xml.xpath.XPathFactory:<uri> pointing to a missing/broken factory class.
Common situations: Typos in XPath (e.g. unbalanced brackets, missing @ on attributes); assuming XPath 2.0+ features (matches(), for-expressions) that JDK 1.0 XPath doesn't support; JVM-wide XPathFactory configuration overriding the default implementation.
Related errors
AI-assisted analysis of jhy/jsoup@9851ac5d9c (2026-09-08).
Data as JSON: /api/errors/81da36ee9aaa77df.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/org/jsoup/helper/W3CDom.java:289
@return the matches nodes
*/
public NodeList selectXpath(String xpath, Node contextNode) {
Validate.notEmptyParam(xpath, "xpath");
Validate.notNullParam(contextNode, "contextNode");
NodeList nodeList;
try {
// if there is a configured XPath factory, use that instead of the Java base impl:
String property = System.getProperty(XPathFactoryProperty);
final XPathFactory xPathFactory = property != null ?
XPathFactory.newInstance("jsoup") :
XPathFactory.newInstance();
XPathExpression expression = xPathFactory.newXPath().compile(xpath);
nodeList = (NodeList) expression.evaluate(contextNode, XPathConstants.NODESET); // love the strong typing here /s
Validate.notNull(nodeList);
} catch (XPathExpressionException | XPathFactoryConfigurationException e) {
throw new Selector.SelectorParseException(
e, "Could not evaluate XPath query [%s]: %s", xpath, e.getMessage());
}
return nodeList;
}
/**
Retrieves the original jsoup DOM nodes from a nodelist created by this convertor.
@param nodeList the W3C nodes to get the original jsoup nodes from
@param nodeType the jsoup node type to retrieve (e.g. Element, DataNode, etc)
@param <T> node type
@return a list of the original nodes
*/
public <T extends org.jsoup.nodes.Node> List<T> sourceNodes(NodeList nodeList, Class<T> nodeType) {
Validate.notNull(nodeList);
Validate.notNull(nodeType);
List<T> nodes = new ArrayList<>(nodeList.getLength());
for (int i = 0; i < nodeList.getLength(); i++) {View on GitHub (pinned to 9851ac5d9c)