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

  1. Check the engine message after the colon for the exact syntax error at the reported offset.
  2. Test the query in a standalone XPath evaluator; fix syntax (e.g. use @attr, correct predicates).
  3. Remove or fix javax.xml.xpath.XPathFactory system properties that install a broken custom factory.
  4. Simplify to XPath 1.0-compatible expressions if using unsupported functions.
  5. 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

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)