jhy/jsoup · error · Selector.SelectorParseException

Could not parse attribute query

Error message

Could not parse attribute query '%s': unexpected token at '%s'

What it means

In an attribute selector [attr op value], jsoup did not recognize the operator after the attribute key, or the value portion was malformed, so evaluatorForAttribute threw Selector.SelectorParseException with the query and the remaining characters. jsoup only supports =, ^=, $=, *=, ~=, |= and !=-style matches plus bare [attr] existence checks.

Solutions

  1. Use a supported operator: [attr=val], [attr^=val], [attr$=val], [attr*=val], [attr~=regex], [attr|=val]
  2. Quote values containing special characters: [href="http://x"]
  3. Check the remainder shown in the message to find the exact offending token and fix it
  4. For complex matching, select by attribute existence and filter with el.attr(...) in code

Example fix

// before
Elements els = doc.select("a[href'foo']");
// after
Elements els = doc.select("a[href='foo']");
Defensive patterns

Strategy: validation

Validate before calling

if (!java.util.regex.Pattern.compile("\\[[^\\]]*(=|\\^=|\\$=|\\*=|~=|\\|=)[^\\]]*\\]").matcher(query).find() && query.matches(".*\\[.*[^=^$*~|-].*")) {
    // flag suspicious attribute selector operators before select()
}

Type guard

boolean hasValidAttrOperator(String q) { return q.matches(".*\\[[\\w-]+(\\^=|\\$=|\\*=|~=|\\|=|=|!=)?[^\\]]*\\].*"); }

Try / catch

try {
    Elements els = doc.select(query);
} catch (Selector.SelectorParseException e) {
    if (e.getMessage().startsWith("Could not parse attribute query")) {
        throw new IllegalArgumentException("Check attribute operator in: " + query, e);
    }
    throw e;
}

Prevention

When it happens

Trigger: A query like doc.select("a[href'foo']") or doc.select("[foo=bar") with an unquoted value containing specials, or an unsupported operator token such as [a !~ b]; the parser hits a token that matches no comparison operator.

Common situations: Hand-written selectors with wrong operator syntax; quotes missing around values with special characters; selectors copied from XPath or other query languages (e.g. [attr!='value'] semantics differences); dynamically built attribute queries.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of jhy/jsoup@9851ac5d9c (2026-09-08). Data as JSON: /api/errors/f85c1ec338075379. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/org/jsoup/select/QueryParser.java:377

            else if (key.equals("*")) // any attribute
                eval = new Evaluator.AttributeStarting("");
            else
                eval = new Evaluator.Attribute(key);
        } else {
            if (cq.matchChomp('='))
                eval = new Evaluator.AttributeWithValue(key, cq.remainder());
            else if (cq.matchChomp("!="))
                eval = new Evaluator.AttributeWithValueNot(key, cq.remainder());
            else if (cq.matchChomp("^="))
                eval = new Evaluator.AttributeWithValueStarting(key, cq.remainder());
            else if (cq.matchChomp("$="))
                eval = new Evaluator.AttributeWithValueEnding(key, cq.remainder());
            else if (cq.matchChomp("*="))
                eval = new Evaluator.AttributeWithValueContaining(key, cq.remainder());
            else if (cq.matchChomp("~="))
                eval = new Evaluator.AttributeWithValueMatching(key, Regex.compile(cq.remainder()));
            else
                throw new Selector.SelectorParseException(
                    "Could not parse attribute query '%s': unexpected token at '%s'", query, cq.remainder());
        }
        return eval;
    }

    //pseudo selectors :first-child, :last-child, :nth-child, ...
    private static final Pattern NthStepOffset = Pattern.compile("(([+-])?(\\d+)?)n(\\s*([+-])?\\s*\\d+)?", Pattern.CASE_INSENSITIVE);
    private static final Pattern NthOffset = Pattern.compile("([+-])?(\\d+)");

    private Evaluator cssNthChild(boolean last, boolean ofType) {
        String arg = asciiLowerCase(trimAsciiWhitespace(consumeParens())); // arg is like "odd", or "-n+2", within nth-child(odd)
        final int step, offset;
        if ("odd".equals(arg)) {
            step = 2;
            offset = 1;
        } else if ("even".equals(arg)) {
            step = 2;
            offset = 0;

View on GitHub (pinned to 9851ac5d9c)