{"record":{"id":"59925fa6b23ec698","repo":"twigphp/Twig","slug":"the-documentation-target-for-a-tag-can-only-be-set-once","errorCode":null,"errorMessage":"The documentation target for a tag can only be set once.","messagePattern":"The documentation target for a tag can only be set once\\.","errorType":"exception","errorClass":"LogicException","httpStatus":null,"severity":"error","filePath":"src/Parser.php","lineNumber":328,"sourceCode":"        }\n\n        $this->blocks[$name] = new BodyNode([$value], [], $value->getTemplateLine());\n    }\n\n    public function hasMacro(string $name): bool\n    {\n        trigger_deprecation('twig/twig', '3.12', 'Method \"%s()\" is deprecated.', __METHOD__);\n\n        return isset($this->macros[$name]);\n    }\n\n    public function setDocumentationTarget(Node $node): void\n    {\n        if (null === $index = array_key_last($this->documentationTargets)) {\n            throw new \\LogicException('A documentation target can only be set while parsing a tag.');\n        }\n        if (null !== $this->documentationTargets[$index]) {\n            throw new \\LogicException('The documentation target for a tag can only be set once.');\n        }\n\n        $this->documentationTargets[$index] = $node;\n    }\n\n    public function setMacro(string $name, MacroNode $node): void\n    {\n        if (isset($this->macros[$name])) {\n            trigger_deprecation('twig/twig', '3.29', 'Defining the macro \"%s\" more than once in \"%s\" is deprecated and will throw a SyntaxError in Twig 4.0 (previous definition at line %d, new definition at line %d). The last definition is used in Twig 3.', $name, $this->stream->getSourceContext()->getName(), $this->macros[$name]->getTemplateLine(), $node->getTemplateLine());\n        }\n\n        $this->macros[$name] = $node;\n    }\n\n    public function addTrait($trait): void\n    {\n        $this->traits[] = $trait;\n    }","sourceCodeStart":310,"sourceCodeEnd":346,"githubUrl":"https://github.com/twigphp/Twig/blob/a414c3a491defb5a60f2fc88ef79ff37c90010cd/src/Parser.php#L310-L346","documentation":"When a tag being parsed opens a documentation target slot, setDocumentationTarget() may be called only once to attach the Node. A second call finds the slot already filled and throws, because a single tag can have at most one documentation target.","triggerScenarios":"Calling $parser->setDocumentationTarget($node) twice within the same tag's parse (e.g. in both the expression parse and the closing logic of a custom TokenParser), or a tag parser that calls it in a loop.","commonSituations":"Custom TokenParser with duplicated wiring code; refactored parsers where the call was accidentally left in two code paths that both execute; base-class + subclass both invoking the method.","solutions":["Ensure setDocumentationTarget() is invoked exactly once per parsed tag; remove duplicate call sites.","Track locally whether the target was already set before calling again.","If two nodes need documentation, restructure so only the outermost/primary node is registered."],"exampleFix":"// before\n$this->parser->setDocumentationTarget($exprNode);\n$this->parser->setDocumentationTarget($blockNode); // second call throws\n\n// after\n$this->parser->setDocumentationTarget($blockNode); // single target only","handlingStrategy":"validation","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { $parser->setDocumentationTarget($node); } catch (\\LogicException $e) { if (str_contains($e->getMessage(), 'can only be set once')) { /* duplicate call site */ } throw $e; }","preventionTips":["Register the documentation target at exactly one call site per TokenParser","Search custom parsers for duplicate setDocumentationTarget calls before release","Use a single code path for tag finalization"],"tags":["twig","php","parser","api-misuse"],"backgroundTag":"invalid-state-transition","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"}