netdata/netdata · error · ConstructorError

found unconstructable recursive node

Error message

found unconstructable recursive node

What it means

Raised by BaseConstructor.construct_object when a node is re-entered while it is still being constructed (it sits in self.recursive_objects). This happens with self-referential structures whose constructor cannot be expressed incrementally — typically when a recursive node appears where a fully constructed object is required, e.g. as a mapping key or inside deep construction of non-generator constructors.

Source

Thrown at src/collectors/python.d.plugin/python_modules/pyyaml3/constructor.py:61

        while self.state_generators:
            state_generators = self.state_generators
            self.state_generators = []
            for generator in state_generators:
                for dummy in generator:
                    pass
        self.constructed_objects = {}
        self.recursive_objects = {}
        self.deep_construct = False
        return data

    def construct_object(self, node, deep=False):
        if node in self.constructed_objects:
            return self.constructed_objects[node]
        if deep:
            old_deep = self.deep_construct
            self.deep_construct = True
        if node in self.recursive_objects:
            raise ConstructorError(None, None,
                    "found unconstructable recursive node", node.start_mark)
        self.recursive_objects[node] = None
        constructor = None
        tag_suffix = None
        if node.tag in self.yaml_constructors:
            constructor = self.yaml_constructors[node.tag]
        else:
            for tag_prefix in self.yaml_multi_constructors:
                if node.tag.startswith(tag_prefix):
                    tag_suffix = node.tag[len(tag_prefix):]
                    constructor = self.yaml_multi_constructors[tag_prefix]
                    break
            else:
                if None in self.yaml_multi_constructors:
                    tag_suffix = node.tag
                    constructor = self.yaml_multi_constructors[None]
                elif None in self.yaml_constructors:
                    constructor = self.yaml_constructors[None]

View on GitHub (pinned to 4864de85e2)

Solutions

  1. Remove the self-reference from the YAML — express shared data with ordinary aliases to a fully-defined node instead of a cyclic one.
  2. If cyclic data is genuinely needed, use objects and a custom constructor with two-step (generator-based) construction, or build the cycle in Python after loading.
  3. Check whether the recursion is accidental (an alias at the wrong indentation pointing at its own parent) and fix the alias target.

Example fix

# before (cyclic -> unconstructable)
a: &x
  b: *x

# after (acyclic sharing)
base: &base {v: 1}
a:
  b: *base
Defensive patterns

Strategy: try-catch

Validate before calling

import yaml

def has_recursive_nodes(text):
    def walk(node, seen):
        if id(node) in seen:
            return True
        seen = seen | {id(node)}
        if isinstance(node, yaml.MappingNode):
            return any(walk(k, seen) or walk(v, seen) for k, v in node.value)
        if isinstance(node, yaml.SequenceNode):
            return any(walk(c, seen) for c in node.value)
        return False
    return walk(yaml.compose(text), frozenset())

Try / catch

try:
    cfg = yaml.safe_load(text)
except yaml.ConstructorError as e:
    if 'unconstructable recursive node' in str(e):
        reject_config('cyclic YAML structures are not supported')
    raise

Prevention

When it happens

Trigger: Self-referential YAML such as '&a {b: *a}' used as a mapping key, or 'a: &x\n b: *x' combined with constructors that build eagerly (deep=True), so the alias is resolved before the parent object exists.

Common situations: Hand-written YAML that models linked/cyclic data (trees with parent links); users copying recursive YAML examples into configs parsed by SafeConstructor; using yaml.compose on cyclic data then constructing with non-safe constructors.

Related errors


AI-assisted analysis of netdata/netdata@4864de85e2 (2026-08-15). Data as JSON: /api/errors/45682760621d2eed. Report an issue: GitHub.