{"record":{"id":"fdae120d84ed40bf","repo":"symfony/css-selector","slug":"expected-a-single-string-or-identifier-for-lang-got","errorCode":null,"errorMessage":"Expected a single string or identifier for :lang(), got ","messagePattern":"Expected a single string or identifier for :lang\\(\\), got ","errorType":"exception","errorClass":"ExpressionErrorException","httpStatus":null,"severity":"error","filePath":"XPath/Extension/FunctionExtension.php","lineNumber":151,"sourceCode":"                throw new ExpressionErrorException('Expected a single string or identifier for :contains(), got '.implode(', ', $arguments));\n            }\n        }\n\n        return $xpath->addCondition(\\sprintf(\n            'contains(string(.), %s)',\n            Translator::getXpathLiteral($arguments[0]->getValue())\n        ));\n    }\n\n    /**\n     * @throws ExpressionErrorException\n     */\n    public function translateLang(XPathExpr $xpath, FunctionNode $function): XPathExpr\n    {\n        $arguments = $function->getArguments();\n        foreach ($arguments as $token) {\n            if (!($token->isString() || $token->isIdentifier())) {\n                throw new ExpressionErrorException('Expected a single string or identifier for :lang(), got '.implode(', ', $arguments));\n            }\n        }\n\n        return $xpath->addCondition(\\sprintf(\n            'lang(%s)',\n            Translator::getXpathLiteral($arguments[0]->getValue())\n        ));\n    }\n\n    public function getName(): string\n    {\n        return 'function';\n    }\n}\n","sourceCodeStart":133,"sourceCodeEnd":166,"githubUrl":"https://github.com/symfony/css-selector/blob/08e2905152a39cf3fd1745d83f8c483e258887d9/XPath/Extension/FunctionExtension.php#L133-L166","documentation":"ExpressionErrorException thrown by FunctionExtension::translateLang (plain XPath extension) when any argument token of :lang() is neither a string nor an identifier. The translator builds a lang(...) XPath condition from a literal value only, so other token types are rejected before the condition is emitted.","triggerScenarios":"Translating selectors like 'p:lang(123)', ':lang(en, fr)' where one of the tokens is not string/identifier, or constructing a FunctionNode with non-literal tokens programmatically and running it through Translator::cssToXPath.","commonSituations":"Unquoted language codes containing characters that tokenize as numbers, generated selectors feeding raw values, confusion with the CSS4 multi-argument :lang() which this library does not support this way.","solutions":["Quote the language code as a string: ':lang(\"en\")'.","Use a plain identifier: ':lang(en)'.","Pass exactly one language per :lang() and combine selectors for multiple languages: ':lang(en), :lang(fr)'.","Catch ExpressionErrorException and validate :lang() arguments before translating."],"exampleFix":"// before\n'p:lang(12)'\n// after\n'p:lang(\"en\")'","handlingStrategy":"validation","validationCode":"foreach ($args as $arg) {\n    if (!preg_match('/^(\"[^\"]*\"|\\'[\\w-]*\\'|[A-Za-z][\\w-]*)$/', $arg)) {\n        throw new \\InvalidArgumentException(':lang() argument must be a quoted string or identifier.');\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    $xpath = $translator->cssToXPath($css);\n} catch (ExpressionErrorException $e) {\n    if (str_contains($e->getMessage(), ':lang()')) {\n        // reject selector\n    }\n}","preventionTips":["Quote language codes: :lang(\"en\").","Pass exactly one language per :lang(); combine selectors for multiple.","Use standard BCP-47-ish tags (letters/hyphens) that tokenize as identifiers.","Validate generated :lang() values before translation."],"tags":["css-selector","lang","argument-validation"],"backgroundTag":"invalid-argument-value","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"}