symfony/css-selector · error · SyntaxErrorException
identifier
Error message
identifier
What it means
TokenStream::getNextIdentifier() consumes the next token and requires it to be an identifier token (e.g. a tag or attribute name like 'div' or 'foo'). If the next token is anything else (a delimiter, string, hash, whitespace at the wrong place, etc.) it throws a Symfony CssSelector SyntaxErrorException created by SyntaxErrorException::unexpectedToken('identifier', $next), reporting the expected type and the offending token.
Solutions
- Fix the CSS selector so a valid identifier appears at that position (identifiers must match [-\w]+ / CSS ident syntax, e.g. 'p.name' not 'p.[').
- Validate or sanitize dynamically built selector strings before passing them to CssSelector (check that interpolated parts are non-empty and identifier-safe).
- Inspect the full exception message: it names the expected token type and the offending token, which pinpoints the exact character in the selector that is wrong.
- If you drive TokenStream directly, check $stream->getPeek()->isIdentifier() before calling getNextIdentifier(), or catch SyntaxErrorException to handle bad input gracefully.
- Wrap user-supplied selectors in try/catch (SyntaxErrorException extends ParseException) and surface a friendly validation message instead of crashing.
Example fix
// before: malformed selector, '.' followed by '['
$crawler->filter('div.[data-x]')->each(...);
// after: valid identifier after the combinator/attribute syntax
$crawler->filter('div[data-x]')->each(...); Defensive patterns
Strategy: validation
Validate before calling
// Check the next token before consuming it
use Symfony\Component\CssSelector\Parser\Token;
function nextTokenIsIdentifier(Symfony\Component\CssSelector\Parser\TokenStream $stream): bool
{
return $stream->getPeek()->isIdentifier();
}
// For raw selector strings: ensure each part is a valid identifier
function isValidCssIdentifier(string $s): bool
{
return preg_match('/^-?[A-Za-z_][A-Za-z0-9_-]*$|^-[A-Za-z_][A-Za-z0-9_-]*$/', $s) === 1;
} Type guard
function isIdentifierToken(Symfony\Component\CssSelector\Parser\Token $t): bool
{
return $t->isIdentifier();
} Try / catch
try {
$value = $stream->getNextIdentifier();
} catch (Symfony\Component\CssSelector\Exception\SyntaxErrorException $e) {
// $e->getMessage() names the offending token; reject the selector input
throw new InvalidArgumentException('Invalid selector: ' . $e->getMessage(), 0, $e);
} Prevention
- Validate user- or config-supplied selector strings against ident syntax before parsing.
- Never interpolate raw variables into selectors; escape or reject values that are not [-\w]+.
- Prefer higher-level APIs (cssToXpath, Crawler::filter) wrapped in try/catch for ParseException.
- When driving TokenStream directly, use getPeek() to type-check tokens before consuming them.
- Log the SyntaxErrorException message in tests for every selector template you generate.
When it happens
Trigger: Calling getNextIdentifier() (directly or through a grammar handler that expects a name) when the token stream's next token is not an identifier: e.g. parsing a selector where a name is missing or misplaced, such as ':attr(' followed by ')' or '=' instead of a name, or a custom parser built on the stream feeding it a hash/string/delimiter token.
Common situations: Passing a malformed CSS selector string to Symfony CssSelector (e.g. via Crawler::filter() or cssToXpath()) such as 'p[' or '::=div'; typos in hand-written selectors; dynamic selectors built from user input or variables that interpolate to empty/illegal names; version changes in tokenizer behavior altering how a character is classified.
Related errors
- identifier or "*"
- Unexpected pseudo-element
- Got immediate child pseudo-element
- ]
- Unclosed/invalid string at
AI-assisted analysis of symfony/css-selector@08e2905152 (2026-09-14).
Data as JSON: /api/errors/2f2bf3be29e0dace.
Report an issue: GitHub.
Appendix: source
Thrown at Parser/TokenStream.php:119
*
* @return Token[]
*/
public function getUsed(): array
{
return $this->used;
}
/**
* Returns next identifier token.
*
* @throws SyntaxErrorException If next token is not an identifier
*/
public function getNextIdentifier(): string
{
$next = $this->getNext();
if (!$next->isIdentifier()) {
throw SyntaxErrorException::unexpectedToken('identifier', $next);
}
return $next->getValue();
}
/**
* Returns next identifier or null if star delimiter token is found.
*
* @throws SyntaxErrorException If next token is not an identifier or a star delimiter
*/
public function getNextIdentifierOrStar(): ?string
{
$next = $this->getNext();
if ($next->isIdentifier()) {
return $next->getValue();
}
View on GitHub (pinned to 08e2905152)