twigphp/Twig · error · LogicException

The documentation target for a tag can only be set once.

Error message

The documentation target for a tag can only be set once.

What it means

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.

Solutions

  1. Ensure setDocumentationTarget() is invoked exactly once per parsed tag; remove duplicate call sites.
  2. Track locally whether the target was already set before calling again.
  3. If two nodes need documentation, restructure so only the outermost/primary node is registered.

Example fix

// before
$this->parser->setDocumentationTarget($exprNode);
$this->parser->setDocumentationTarget($blockNode); // second call throws

// after
$this->parser->setDocumentationTarget($blockNode); // single target only
Defensive patterns

Strategy: validation

Try / catch

try { $parser->setDocumentationTarget($node); } catch (\LogicException $e) { if (str_contains($e->getMessage(), 'can only be set once')) { /* duplicate call site */ } throw $e; }

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of twigphp/Twig@a414c3a491 (2026-09-13). Data as JSON: /api/errors/59925fa6b23ec698. Report an issue: GitHub.

Appendix: source

Thrown at src/Parser.php:328

        }

        $this->blocks[$name] = new BodyNode([$value], [], $value->getTemplateLine());
    }

    public function hasMacro(string $name): bool
    {
        trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__);

        return isset($this->macros[$name]);
    }

    public function setDocumentationTarget(Node $node): void
    {
        if (null === $index = array_key_last($this->documentationTargets)) {
            throw new \LogicException('A documentation target can only be set while parsing a tag.');
        }
        if (null !== $this->documentationTargets[$index]) {
            throw new \LogicException('The documentation target for a tag can only be set once.');
        }

        $this->documentationTargets[$index] = $node;
    }

    public function setMacro(string $name, MacroNode $node): void
    {
        if (isset($this->macros[$name])) {
            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());
        }

        $this->macros[$name] = $node;
    }

    public function addTrait($trait): void
    {
        $this->traits[] = $trait;
    }

View on GitHub (pinned to a414c3a491)