twigphp/Twig · error · SyntaxError
Expected endblock for block
Error message
Expected endblock for block "%s" (but "%s" given).
What it means
Thrown by BlockTokenParser::parse when a {% block %} is closed with {% endblock name %} whose name does not match the opening block's name. Twig checks the optional name after endblock for consistency and raises a SyntaxError pointing at the endblock line. The mismatch almost always indicates a copy-paste or nesting mistake.
Solutions
- Change the name after endblock to match the opening block name.
- Remove the name entirely: plain {% endblock %} is always valid.
- Check block nesting — the mismatched endblock may be closing the wrong block.
- Rename the opening {% block %} if the endblock name is the intended one.
Example fix
{% block title %}Hello{% endblock header %}
{# after #}
{% block title %}Hello{% endblock %} Defensive patterns
Strategy: validation
Try / catch
try {
$twig->parse($twig->tokenize(new \Twig\Source($code, $name)));
} catch (\Twig\Error\SyntaxError $e) {
if (str_contains($e->getMessage(), 'Expected endblock')) {
// surface template name/line in build output
}
throw $e;
} Prevention
- Prefer plain {% endblock %} with no name label.
- Enable editor linting for unbalanced/mismatched block tags.
- When copy-pasting blocks, rename both opening and closing tags together.
When it happens
Trigger: Writing {% block title %}...{% endblock header %} — the NAME token after endblock differs from the opening block name (loose != comparison, so case differs count as mismatch only for different strings).
Common situations: Copy-pasting blocks and forgetting to update the endblock label; misreading nesting where an inner endblock closes the wrong block; automated refactors that renamed the opening tag only.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Expected endmacro for macro
- Block " " on template " " does not exist.
- An exception has been thrown during the compilation of a…
- Calling the "parent" function outside of a block is…
- Calling the "parent" function on a template that does not…
AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13).
Data as JSON: /api/errors/f135f9f18069fc60.
Report an issue: GitHub.
Appendix: source
Thrown at src/TokenParser/BlockTokenParser.php:52
final class BlockTokenParser extends AbstractTokenParser
{
public function parse(Token $token): Node
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
$name = $stream->expect(Token::NAME_TYPE)->getValue();
$this->parser->setBlock($name, $block = new BlockNode($name, new EmptyNode(), $lineno));
$this->parser->setDocumentationTarget($block);
$this->parser->pushLocalScope();
$this->parser->pushBlockStack($name);
if ($stream->nextIf(Token::BLOCK_END_TYPE)) {
$body = $this->parser->subparse([$this, 'decideBlockEnd'], true);
if ($token = $stream->nextIf(Token::NAME_TYPE)) {
$value = $token->getValue();
if ($value != $name) {
throw new SyntaxError(\sprintf('Expected endblock for block "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getSourceContext());
}
}
} else {
$body = new Nodes([
new PrintNode($this->parser->parseExpression(), $lineno),
]);
}
$stream->expect(Token::BLOCK_END_TYPE);
$block->setNode('body', $body);
$this->parser->popBlockStack();
$this->parser->popLocalScope();
return new BlockReferenceNode($name, $lineno);
}
public function decideBlockEnd(Token $token): bool
{View on GitHub (pinned to a414c3a491)