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
- Use a supported operator: [attr=val], [attr^=val], [attr$=val], [attr*=val], [attr~=regex], [attr|=val]
- Quote values containing special characters: [href="http://x"]
- Check the remainder shown in the message to find the exact offending token and fix it
- 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
- Quote attribute values with special characters
- Use only =, ^=, $=, *=, ~=, |= operators
- Do not copy XPath predicate syntax into CSS selectors
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Could not parse query
- Could not parse nth-index
- :matchText is no longer supported. Use…
- The supplied URL, ' ', is malformed. Make sure it is an…
- You must execute the request before getting a response.
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)