jhy/jsoup · error · IllegalArgumentException

CSS identifier expected, but end of input found

Error message

CSS identifier expected, but end of input found

What it means

Selector-syntax error raised by TokenQueue.consumeCssIdentifier: the queue was positioned at a CSS identifier (e.g. after a '#' or '.' in a selector) but input ended before any identifier character appeared. Typically caused by a truncated query such as "div#" or ".class>" with nothing following, i.e. malformed user-supplied CSS rather than an internal bug.

Solutions

  1. Validate/complete the CSS selector string before passing it to jsoup; ensure every '#'/'.' prefix is followed by a name.
  2. Catch Selector.SelectorParseException around Selector.parse and surface a 'selector is incomplete' message to the user.
  3. Escape special characters in identifiers (e.g. numeric-leading IDs as \31) so the identifier consumes at least one character.
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at src/main/java/org/jsoup/parser/TokenQueue.java:327 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/1e26d2f97151eaa5. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/org/jsoup/parser/TokenQueue.java:327

     */
    public String consumeElementSelector() {
        return consumeEscapedCssIdentifier(ElementSelectorChars);
    }
    private static final char[] ElementSelectorChars = {'*', '|', '_', '-'};

    /**
     Consume a CSS identifier (ID or class) off the queue.
     <p>Note: For backwards compatibility this method supports improperly formatted CSS identifiers, e.g. {@code 1} instead
     of {@code \31}.</p>

     @return The unescaped identifier.
     @throws IllegalArgumentException if an invalid escape sequence was found. Afterward, the state of the TokenQueue
     is undefined.
     @see <a href="https://www.w3.org/TR/css-syntax-3/#consume-name">CSS Syntax Module Level 3, Consume an ident sequence</a>
     @see <a href="https://www.w3.org/TR/css-syntax-3/#typedef-ident-token">CSS Syntax Module Level 3, ident-token</a>
     */
    public String consumeCssIdentifier() {
        if (isEmpty()) throw new IllegalArgumentException("CSS identifier expected, but end of input found");

        // Fast path for CSS identifiers that don't contain escape sequences.
        String identifier = reader.consumeMatching(TokenQueue::isIdent);
        char c = current();
        if (c != Esc && c != Unicode_Null) {
            // If we didn't end on an Esc or a Null, we consumed the whole identifier
            return identifier;
        }

        // An escape sequence was found. Use a StringBuilder to store the decoded CSS identifier.
        StringBuilder out = StringUtil.borrowBuilder();
        if (!identifier.isEmpty()) {
            // Copy the CSS identifier up to the first escape sequence.
            out.append(identifier);
        }

        while (!isEmpty()) {
            c = current();

View on GitHub (pinned to 9851ac5d9c)