twigphp/Twig · error · SyntaxError
%sUnexpected token " " (" " expected ).
Error message
%sUnexpected token "%s"%s ("%s" expected%s). What it means
TokenStream::expect() is Twig's generic token-mismatch error: the parser encountered a token of unexpected type/value. The message is prefixed with any custom message (e.g. 'An argument must be a name') and shows what was found versus what was expected.
Solutions
- Read the 'Unexpected token X (Y expected)' message and fix the token at the given line to match what was expected
- Run a Twig linter on the template to locate the exact mismatch quickly
- Check that custom message prefixes in the error for hints about which construct was being parsed (argument list, tag name, etc.)
Example fix
// before
{% for user in users if user.active %}
// after
{% for user in users|filter(u => u.active) %} Defensive patterns
Strategy: try-catch
Validate before calling
$env->parse($env->tokenize(new Source($template, $name))); // lint before deploy
Try / catch
try {
$html = $twig->render($name, $context);
} catch (SyntaxError $e) {
$context = [
'message' => $e->getMessage(),
'line' => $e->getTemplateLine(),
'source' => $e->getSourceContext()->getCode(),
];
// render an editor-facing syntax error page instead of a 500
} Prevention
- Run a Twig linter in CI and in editors (twig-language plugins)
- Check the 'X expected' part of the message to know exactly which token to fix
- Avoid editing production templates by hand; use review + lint workflow
When it happens
Trigger: Any syntax deviation the parser checks with expect(): e.g. '{% macro 123() %}' (argument not a name), missing commas/parentheses in expressions or argument lists, wrong tag spelling so a closing tag is expected but not found.
Common situations: Typos in tag names and operators; using reserved keywords as variable names; missing '(', ')', ',', '=', or '%}' in expressions; editing templates by hand without linting.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- The template references in a "use" statement must be a…
- The variadic argument
- The variadic argument
- Unexpected character
- Unexpected end of template.
AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13).
Data as JSON: /api/errors/bc5e671e183abf7f.
Report an issue: GitHub.
Appendix: source
Thrown at src/TokenStream.php:77
/**
* Tests a token, sets the pointer to the next one and returns it or throws a syntax error.
*
* @return Token|null The next token if the condition is true, null otherwise
*/
public function nextIf($primary, $secondary = null)
{
return $this->tokens[$this->current]->test($primary, $secondary) ? $this->next() : null;
}
/**
* Tests a token and returns it or throws a syntax error.
*/
public function expect($type, $value = null, ?string $message = null): Token
{
$token = $this->tokens[$this->current];
if (!$token->test($type, $value)) {
$line = $token->getLine();
throw new SyntaxError(\sprintf('%sUnexpected token "%s"%s ("%s" expected%s).',
$message ? $message.'. ' : '',
$token->toEnglish(),
$token->getValue() ? \sprintf(' of value "%s"', $token->getValue()) : '',
Token::typeToEnglish($type), $value ? \sprintf(' with value "%s"', $value) : ''),
$line,
$this->source,
columnno: $this->source->getColumn($token->getOffset() ?? -1),
);
}
$this->next();
return $token;
}
/**
* Looks at the next token.
*/
public function look(int $number = 1): TokenView on GitHub (pinned to a414c3a491)