jhy/jsoup · error · Selector.SelectorParseException
Unknown combinator
Error message
Unknown combinator '%s'
What it means
Grammar error in QueryParser.combinator: the parser read a combinator character between two simple selectors that is not one of '>', ' ', '+', or '~'. This means the selector contained an unsupported binary operator at a combinator position — an invalid user-supplied CSS query, not an internal fault.
Solutions
- Correct the selector to use only the supported combinators: descendant (whitespace), '>' (child), '+' (adjacent sibling), '~' (general sibling).
- Catch Selector.SelectorParseException and validate combinator characters before passing user input to Selector.parse.
- Check for typos or stray characters (e.g. '<', '>>') in the query between compound selectors.
Defensive patterns
Strategy: try-catch
When it happens
Trigger: Thrown at src/main/java/org/jsoup/select/QueryParser.java:166 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of jhy/jsoup@9851ac5d9c (2026-09-08).
Data as JSON: /api/errors/31f94eb36e139ade.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/org/jsoup/select/QueryParser.java:166
throw new Selector.SelectorParseException("Could not parse query '%s': unexpected token at '%s'", query, tq.remainder());
return left;
}
static Evaluator combinator(Evaluator left, char combinator, Evaluator right) {
switch (combinator) {
case '>':
ImmediateParentRun run = left instanceof ImmediateParentRun ?
(ImmediateParentRun) left : new ImmediateParentRun(left);
run.add(right);
return run;
case ' ':
return and(new StructuralEvaluator.Ancestor(left), right);
case '+':
return and(new StructuralEvaluator.ImmediatePreviousSibling(left), right);
case '~':
return and(new StructuralEvaluator.PreviousSibling(left), right);
default:
throw new Selector.SelectorParseException("Unknown combinator '%s'", combinator);
}
}
@Nullable Evaluator parseSubclass() {
// Subclass: ID | Class | Attribute | Pseudo
if (tq.matchChomp('#')) return byId();
else if (tq.matchChomp('.')) return byClass();
else if (tq.matches('[')) return byAttribute();
else if (tq.matchChomp("::")) return parseNodeSelector(); // ::comment etc
else if (tq.matchChomp(':')) return parsePseudoSelector();
else return null;
}
/** Merge two evals into an Or. */
static Evaluator or(Evaluator left, Evaluator right) {
if (left instanceof CombiningEvaluator.Or) {
((CombiningEvaluator.Or) left).add(right);
return left;View on GitHub (pinned to 9851ac5d9c)