{"record":{"id":"9a4963ac73331eaf","repo":"twigphp/Twig","slug":"expected-endmacro-for-macro-s-but-s-given","errorCode":null,"errorMessage":"Expected endmacro for macro \"%s\" (but \"%s\" given).","messagePattern":"Expected endmacro for macro \"(.+?)\" \\(but \"(.+?)\" given\\)\\.","errorType":"exception","errorClass":"SyntaxError","httpStatus":null,"severity":"error","filePath":"src/TokenParser/MacroTokenParser.php","lineNumber":53,"sourceCode":" * @internal\n */\nfinal class MacroTokenParser extends AbstractTokenParser\n{\n    public function parse(Token $token): Node\n    {\n        $lineno = $token->getLine();\n        $stream = $this->parser->getStream();\n        $name = $stream->expect(Token::NAME_TYPE)->getValue();\n        [$arguments, $variadicName] = $this->parseDefinition($name);\n\n        $stream->expect(Token::BLOCK_END_TYPE);\n        $this->parser->pushLocalScope();\n        $body = $this->parser->subparse([$this, 'decideBlockEnd'], true);\n        if ($token = $stream->nextIf(Token::NAME_TYPE)) {\n            $value = $token->getValue();\n\n            if ($value != $name) {\n                throw new SyntaxError(\\sprintf('Expected endmacro for macro \"%s\" (but \"%s\" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getSourceContext());\n            }\n        }\n        $this->parser->popLocalScope();\n        $stream->expect(Token::BLOCK_END_TYPE);\n\n        $this->parser->setMacro($name, $macro = new MacroNode($name, new BodyNode([$body]), $arguments, $lineno, $variadicName));\n        $this->parser->setDocumentationTarget($macro);\n\n        return new MacroDeclarationNode($name, $lineno);\n    }\n\n    public function decideBlockEnd(Token $token): bool\n    {\n        return $token->test('endmacro');\n    }\n\n    public function getTag(): string\n    {","sourceCodeStart":35,"sourceCodeEnd":71,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/TokenParser/MacroTokenParser.php#L35-L71","documentation":"Thrown by MacroTokenParser::parse when a {% macro %} is closed with {% endmacro name %} whose name does not match the opening macro's name. Like endblock, Twig validates the optional label after endmacro and raises a SyntaxError on mismatch, pointing at the current line.","triggerScenarios":"Writing {% macro input(field) %}...{% endmacro textbox %} — the NAME token after endmacro differs from the macro's declared name (loose != comparison).","commonSituations":"Copy-pasting macros and forgetting to rename endmacro; renaming the macro declaration but not the closing tag; nesting confusion inside {% import %}-heavy template libraries.","solutions":["Make the endmacro name match the macro name.","Drop the name entirely and just use {% endmacro %}.","Verify nesting — the mismatched endmacro may belong to a different macro scope."],"exampleFix":"{% macro input(name) %}...{% endmacro textbox %}\n\n{# after #}\n{% macro input(name) %}...{% endmacro %}","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    $twig->parse($twig->tokenize(new \\Twig\\Source($code, $name)));\n} catch (\\Twig\\Error\\SyntaxError $e) {\n    if (str_contains($e->getMessage(), 'Expected endmacro')) {\n        // align endmacro label with the macro name\n    }\n    throw $e;\n}","preventionTips":["Prefer plain {% endmacro %} without a name label.","Rename macro declarations and closing tags together.","Lint macro-heavy template libraries in CI."],"tags":["twig","macro","syntax-error","template-parsing"],"backgroundTag":"invalid-argument-value","analyzedSha":"a414c3a491defb5a60f2fc88ef79ff37c90010cd","analyzedAt":"2026-09-13T15:10:46.849Z","contentChangedAt":"2026-09-13T15:10:46.849Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}