twigphp/Twig · error · LogicException

Attribute " " does not exist for Node " ".

Error message

Attribute "%s" does not exist for Node "%s".

What it means

Node::getAttribute() throws when the requested attribute key does not exist on the node's $attributes array. Twig nodes store metadata (names, values, flags) in attributes; requesting a key the node never had means the caller assumes a node shape that differs from the actual node. It is a LogicException signaling a programming error in a compiler, visitor, or extension.

Solutions

  1. Call $node->hasAttribute('name') before getAttribute(), or use $node->getAttribute('name', false) where deprecation-triggering argument applies.
  2. Check the actual node class (get_class($node)) and only request attributes that class defines.
  3. If handling multiple node types, branch on instanceof before accessing attributes.
  4. If caused by a Twig upgrade, consult the CHANGELOG for renamed node attributes and update custom code.

Example fix

// before
$name = $node->getAttribute('name');
// after
$name = $node->hasAttribute('name') ? $node->getAttribute('name') : null;
Defensive patterns

Strategy: type-guard

Validate before calling

if (!$node->hasAttribute('name')) {
    throw new \RuntimeException('Unexpected node shape: no "name" attribute on ' . get_class($node));
}

Type guard

$attr = $node->hasAttribute($name) ? $node->getAttribute($name) : null;

Try / catch

try {
    $value = $node->getAttribute('name');
} catch (\LogicException $e) {
    $value = null; // attribute absent for this node type
}

Prevention

When it happens

Trigger: Calling $node->getAttribute('name') on a node type that has no 'name' attribute, e.g. calling NodeExpression Name methods on a ConstantExpression, or a custom visitor/compiler pass assuming every node carries attributes like 'value', 'raw', or 'always_defined'.

Common situations: Custom Twig extensions or dump-style debug visitors inspecting heterogeneous nodes and assuming a uniform attribute set; upgrading Twig where a node attribute was renamed/removed (attribute deprecations exist in this code) while custom code still requests the old name; copy-pasted compiler code applied to the wrong node class.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


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

Appendix: source

Thrown at src/Node/Node.php:173

     */
    public function setNodeTag(string $tag): void
    {
        if ($this->tag) {
            throw new \LogicException('The tag of a node can only be set once.');
        }

        $this->tag = $tag;
    }

    public function hasAttribute(string $name): bool
    {
        return \array_key_exists($name, $this->attributes);
    }

    public function getAttribute(string $name)
    {
        if (!\array_key_exists($name, $this->attributes)) {
            throw new \LogicException(\sprintf('Attribute "%s" does not exist for Node "%s".', $name, static::class));
        }

        $triggerDeprecation = \func_num_args() > 1 ? func_get_arg(1) : true;
        if ($triggerDeprecation && isset($this->attributeNameDeprecations[$name])) {
            $dep = $this->attributeNameDeprecations[$name];
            if ($dep->getNewName()) {
                trigger_deprecation($dep->getPackage(), $dep->getVersion(), 'Getting attribute "%s" on a "%s" class is deprecated, get the "%s" attribute instead.', $name, static::class, $dep->getNewName());
            } else {
                trigger_deprecation($dep->getPackage(), $dep->getVersion(), 'Getting attribute "%s" on a "%s" class is deprecated.', $name, static::class);
            }
        }

        return $this->attributes[$name];
    }

    public function setAttribute(string $name, $value): void
    {
        $triggerDeprecation = \func_num_args() > 2 ? func_get_arg(2) : true;

View on GitHub (pinned to a414c3a491)