jhy/jsoup · error · Selector.SelectorParseException
Could not parse nth-index
Error message
Could not parse nth-index '%s': unexpected format
What it means
The argument to :nth-child(), :nth-last-child(), :nth-of-type() or :nth-last-of-type() did not match any of jsoup's accepted forms (odd/even/an+b, plain integers, or +n offsets), so cssNthChild threw Selector.SelectorParseException. The nth expression grammar is strict: whitespace-free an+b patterns or simple integers.
Solutions
- Rewrite the nth expression in canonical form: odd, even, an integer, or 'an+b' like 2n+1 (no spaces)
- Verify the interpolated variable actually contains a valid nth expression before building the selector
- Catch Selector.SelectorParseException around select() to surface the bad argument to users
- Compute the element by index in Java (children().get(i)) if the pattern is too complex
Example fix
// before
Elements els = doc.select("li:nth-child(2 n + 1)");
// after
Elements els = doc.select("li:nth-child(2n+1)"); Defensive patterns
Strategy: validation
Validate before calling
if (!arg.matches("(odd|even|\\d+|[+-]?\\d*n([+-]\\d+)?|\\d+n)")) {
throw new IllegalArgumentException("Invalid nth expression: " + arg);
} Type guard
boolean isValidNthArg(String arg) {
return arg != null && arg.trim().matches("(odd|even|[+-]?\\d*n([+-]\\d+)?|\\d+)");
} Try / catch
try {
Elements els = doc.select("li:nth-child(" + arg + ")");
} catch (Selector.SelectorParseException e) {
if (e.getMessage().contains("nth-index")) {
throw new IllegalArgumentException("nth expression must be odd/even/an+b: " + arg, e);
}
throw e;
} Prevention
- Keep nth expressions in canonical an+b form with no spaces
- Sanitize interpolated values before building selectors
- Prefer odd/even/integer forms where possible
When it happens
Trigger: doc.select("td:nth-child(2n+1 ") with stray spaces/characters, doc.select("li:nth-child(one)"), doc.select("div:nth-child(2 n + 1)") or any non-numeric, non-odd/even expression. Parsed in QueryParser.cssNthChild.
Common situations: Typos or extra whitespace inside nth parentheses; selectors generated by template code interpolating bad values; copying nth expressions from browsers that tolerate formats jsoup's regex does not.
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
- Could not parse query
- Could not parse attribute query
- :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/3f75951be68fa399.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/java/org/jsoup/select/QueryParser.java:409
step = 2;
offset = 1;
} else if ("even".equals(arg)) {
step = 2;
offset = 0;
} else {
Matcher stepOffsetM, stepM;
if ((stepOffsetM = NthStepOffset.matcher(arg)).matches()) {
if (stepOffsetM.group(3) != null) // has digits, like 3n+2 or -3n+2
step = Integer.parseInt(stepOffsetM.group(1).replaceFirst("^\\+", ""));
else // no digits, might be like n+2, or -n+2. if group(2) == "-", it’s -1;
step = "-".equals(stepOffsetM.group(2)) ? -1 : 1;
offset =
stepOffsetM.group(4) != null ? Integer.parseInt(stepOffsetM.group(4).replaceFirst("^\\+", "")) : 0;
} else if ((stepM = NthOffset.matcher(arg)).matches()) {
step = 0;
offset = Integer.parseInt(stepM.group().replaceFirst("^\\+", ""));
} else {
throw new Selector.SelectorParseException("Could not parse nth-index '%s': unexpected format", arg);
}
}
return ofType
? (last ? new Evaluator.IsNthLastOfType(step, offset) : new Evaluator.IsNthOfType(step, offset))
: (last ? new Evaluator.IsNthLastChild(step, offset) : new Evaluator.IsNthChild(step, offset));
}
private String consumeParens() {
return tq.chompBalanced('(', ')');
}
private int consumeIndex() {
String index = consumeParens().trim();
Validate.isTrue(StringUtil.isNumeric(index), "Index must be numeric");
return Integer.parseInt(index);
}
View on GitHub (pinned to 9851ac5d9c)