jhy/jsoup · error · IllegalArgumentException

Invalid escape sequence:

Error message

Invalid escape sequence: 

What it means

Selector-syntax error from TokenQueue's CSS escape handling (consumeCssEscapeSequenceInto): a backslash escape in an identifier did not resolve to a valid character — e.g. a malformed hex escape like "\xZZ", an empty escape at end of input, or a hex value that cannot form a valid code point. The input at fault is the escaped identifier text in the user's CSS selector.

Solutions

  1. Use well-formed CSS escapes: backslash + up to 6 hex digits, or backslash + a literal non-hex character.
  2. Catch Selector.SelectorParseException and report the offending selector to the user instead of letting it propagate.
  3. Prefer correctly escaped identifiers (e.g. \31 for leading digits) over raw unsupported syntax.
Defensive patterns

Strategy: validation

When it happens

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

Appendix: source

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

    }

    private void consumeCssEscapeSequenceInto(StringBuilder out) {
        if (isEmpty()) {
            out.append(Replacement);
            return;
        }

        char firstEscaped = consume();
        if (!StringUtil.isHexDigit(firstEscaped)) {
            out.append(firstEscaped);
        } else {
            reader.unconsume(); // put back the first hex digit
            String hexString = reader.consumeMatching(StringUtil::isHexDigit, 6); // consume up to 6 hex digits
            int codePoint;
            try {
                codePoint = Integer.parseInt(hexString, 16);
            } catch (NumberFormatException e) {
                throw new IllegalArgumentException("Invalid escape sequence: " + hexString, e);
            }
            if (isValidCodePoint(codePoint)) {
                out.appendCodePoint(codePoint);
            } else {
                out.append(Replacement);
            }

            if (!isEmpty()) {
                char c = current();
                if (c == '\r') {
                    // Since there's currently no input preprocessing, check for CRLF here.
                    // https://www.w3.org/TR/css-syntax-3/#input-preprocessing
                    advance();
                    if (!isEmpty() && current() == '\n') advance();
                } else if (c == ' ' || c == '\t' || isNewline(c)) {
                    advance();
                }
            }

View on GitHub (pinned to 9851ac5d9c)