{"record":{"id":"513da725dd341616","repo":"symfony/css-selector","slug":"unclosed-invalid-string-at-s","errorCode":null,"errorMessage":"Unclosed/invalid string at %s.","messagePattern":"Unclosed/invalid string at (.+?)\\.","errorType":"exception","errorClass":"SyntaxErrorException","httpStatus":null,"severity":"error","filePath":"Parser/Handler/StringHandler.php","lineNumber":57,"sourceCode":"\n    public function handle(Reader $reader, TokenStream $stream): bool\n    {\n        $quote = $reader->getSubstring(1);\n\n        if (!\\in_array($quote, [\"'\", '\"'], true)) {\n            return false;\n        }\n\n        $reader->moveForward(1);\n        $match = $reader->findPattern($this->patterns->getQuotedStringPattern($quote));\n\n        if (!$match) {\n            throw new InternalErrorException(\\sprintf('Should have found at least an empty match at %d.', $reader->getPosition()));\n        }\n\n        // check unclosed strings\n        if (\\strlen($match[0]) === $reader->getRemainingLength()) {\n            throw SyntaxErrorException::unclosedString($reader->getPosition() - 1);\n        }\n\n        // check quotes pairs validity\n        if ($quote !== $reader->getSubstring(1, \\strlen($match[0]))) {\n            throw SyntaxErrorException::unclosedString($reader->getPosition() - 1);\n        }\n\n        $string = $this->escaping->escapeUnicodeAndNewLine($match[0]);\n        $stream->push(new Token(Token::TYPE_STRING, $string, $reader->getPosition()));\n        $reader->moveForward(\\strlen($match[0]) + 1);\n\n        return true;\n    }\n}\n","sourceCodeStart":39,"sourceCodeEnd":72,"githubUrl":"https://github.com/symfony/css-selector/blob/08e2905152a39cf3fd1745d83f8c483e258887d9/Parser/Handler/StringHandler.php#L39-L72","documentation":"StringHandler::handle() tokenizes quoted strings in selector input. After matching an opening quote, it validates the string is properly closed: if the match reaches the end of the remaining input, SyntaxErrorException::unclosedString() is thrown because the closing quote is missing. The %s in the raw message is the reader position where the string started.","triggerScenarios":"Passing a selector string containing an unterminated quoted literal to cssToXPath()/parse(), e.g. \"a[href=\\\"foo\\\"\" missing the final quote — the string token runs to end-of-input.","commonSituations":"Dynamically built selector strings where interpolated values contained quotes that broke the literal; user-supplied CSS selectors copied incompletely; template concatenation dropping a closing quote.","solutions":["Add the missing closing quote to the string literal in the selector.","Escape or strip embedded quotes when interpolating values into selectors (e.g. remove \" characters from attribute values).","Validate selector strings with a try/catch around cssToXPath() before using them in production paths."],"exampleFix":"// before\n$css = \"a[href=\\\"example]\"; // unclosed string\n\n// after\n$css = \"a[href=\\\"example\\\"]\"; // properly closed","handlingStrategy":"validation","validationCode":"// ensure quotes are balanced before handing the selector to the parser\nfunction quotesBalanced(string $css): bool {\n    $dq = substr_count($css, '\"'); $sq = substr_count($css, \"'\");\n    return $dq % 2 === 0 && $sq % 2 === 0;\n}\nif (!quotesBalanced($css)) { throw new \\InvalidArgumentException('Unbalanced quotes in selector'); }","typeGuard":null,"tryCatchPattern":"use Symfony\\Component\\CssSelector\\Exception\\SyntaxErrorException;\ntry {\n    $xpath = $translator->cssToXPath($css);\n} catch (SyntaxErrorException $e) {\n    if (str_contains($e->getMessage(), 'Unclosed string') || str_contains($e->getMessage(), 'unclosedString')) {\n        // report bad selector input to caller\n    }\n    throw $e;\n}","preventionTips":["Escape or strip quote characters from dynamic values interpolated into selectors","Validate user-supplied selectors for balanced quotes before parsing","Prefer building selectors with addAttributeMatching APIs instead of string concatenation"],"tags":["css-selector","parser","syntax-error","unclosed-string"],"backgroundTag":"invalid-argument-format","analyzedSha":"08e2905152a39cf3fd1745d83f8c483e258887d9","analyzedAt":"2026-09-14T11:41:28.509Z","contentChangedAt":"2026-09-14T11:41:28.509Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}