{"record":{"id":"f85c1ec338075379","repo":"jhy/jsoup","slug":"could-not-parse-attribute-query-s-unexpected-t","errorCode":null,"errorMessage":"Could not parse attribute query '%s': unexpected token at '%s'","messagePattern":"Could not parse attribute query '(.+?)': unexpected token at '(.+?)'","errorType":"exception","errorClass":"Selector.SelectorParseException","httpStatus":null,"severity":"error","filePath":"src/main/java/org/jsoup/select/QueryParser.java","lineNumber":377,"sourceCode":"            else if (key.equals(\"*\")) // any attribute\n                eval = new Evaluator.AttributeStarting(\"\");\n            else\n                eval = new Evaluator.Attribute(key);\n        } else {\n            if (cq.matchChomp('='))\n                eval = new Evaluator.AttributeWithValue(key, cq.remainder());\n            else if (cq.matchChomp(\"!=\"))\n                eval = new Evaluator.AttributeWithValueNot(key, cq.remainder());\n            else if (cq.matchChomp(\"^=\"))\n                eval = new Evaluator.AttributeWithValueStarting(key, cq.remainder());\n            else if (cq.matchChomp(\"$=\"))\n                eval = new Evaluator.AttributeWithValueEnding(key, cq.remainder());\n            else if (cq.matchChomp(\"*=\"))\n                eval = new Evaluator.AttributeWithValueContaining(key, cq.remainder());\n            else if (cq.matchChomp(\"~=\"))\n                eval = new Evaluator.AttributeWithValueMatching(key, Regex.compile(cq.remainder()));\n            else\n                throw new Selector.SelectorParseException(\n                    \"Could not parse attribute query '%s': unexpected token at '%s'\", query, cq.remainder());\n        }\n        return eval;\n    }\n\n    //pseudo selectors :first-child, :last-child, :nth-child, ...\n    private static final Pattern NthStepOffset = Pattern.compile(\"(([+-])?(\\\\d+)?)n(\\\\s*([+-])?\\\\s*\\\\d+)?\", Pattern.CASE_INSENSITIVE);\n    private static final Pattern NthOffset = Pattern.compile(\"([+-])?(\\\\d+)\");\n\n    private Evaluator cssNthChild(boolean last, boolean ofType) {\n        String arg = asciiLowerCase(trimAsciiWhitespace(consumeParens())); // arg is like \"odd\", or \"-n+2\", within nth-child(odd)\n        final int step, offset;\n        if (\"odd\".equals(arg)) {\n            step = 2;\n            offset = 1;\n        } else if (\"even\".equals(arg)) {\n            step = 2;\n            offset = 0;","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/jhy/jsoup/blob/9851ac5d9c576c6888910b5a51a2362bbc978959/src/main/java/org/jsoup/select/QueryParser.java#L359-L395","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nElements els = doc.select(\"a[href'foo']\");\n// after\nElements els = doc.select(\"a[href='foo']\");","handlingStrategy":"validation","validationCode":"if (!java.util.regex.Pattern.compile(\"\\\\[[^\\\\]]*(=|\\\\^=|\\\\$=|\\\\*=|~=|\\\\|=)[^\\\\]]*\\\\]\").matcher(query).find() && query.matches(\".*\\\\[.*[^=^$*~|-].*\")) {\n    // flag suspicious attribute selector operators before select()\n}","typeGuard":"boolean hasValidAttrOperator(String q) { return q.matches(\".*\\\\[[\\\\w-]+(\\\\^=|\\\\$=|\\\\*=|~=|\\\\|=|=|!=)?[^\\\\]]*\\\\].*\"); }","tryCatchPattern":"try {\n    Elements els = doc.select(query);\n} catch (Selector.SelectorParseException e) {\n    if (e.getMessage().startsWith(\"Could not parse attribute query\")) {\n        throw new IllegalArgumentException(\"Check attribute operator in: \" + query, e);\n    }\n    throw e;\n}","preventionTips":["Quote attribute values with special characters","Use only =, ^=, $=, *=, ~=, |= operators","Do not copy XPath predicate syntax into CSS selectors"],"tags":["jsoup","css-selector","attribute-selector","parse-error"],"backgroundTag":"invalid-argument-format","analyzedSha":"9851ac5d9c576c6888910b5a51a2362bbc978959","analyzedAt":"2026-09-08T15:22:04.931Z","contentChangedAt":"2026-09-08T15:22:04.931Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}