{"record":{"id":"45682760621d2eed","repo":"netdata/netdata","slug":"found-unconstructable-recursive-node","errorCode":null,"errorMessage":"found unconstructable recursive node","messagePattern":"found unconstructable recursive node","errorType":"exception","errorClass":"ConstructorError","httpStatus":null,"severity":"error","filePath":"src/collectors/python.d.plugin/python_modules/pyyaml3/constructor.py","lineNumber":61,"sourceCode":"        while self.state_generators:\n            state_generators = self.state_generators\n            self.state_generators = []\n            for generator in state_generators:\n                for dummy in generator:\n                    pass\n        self.constructed_objects = {}\n        self.recursive_objects = {}\n        self.deep_construct = False\n        return data\n\n    def construct_object(self, node, deep=False):\n        if node in self.constructed_objects:\n            return self.constructed_objects[node]\n        if deep:\n            old_deep = self.deep_construct\n            self.deep_construct = True\n        if node in self.recursive_objects:\n            raise ConstructorError(None, None,\n                    \"found unconstructable recursive node\", node.start_mark)\n        self.recursive_objects[node] = None\n        constructor = None\n        tag_suffix = None\n        if node.tag in self.yaml_constructors:\n            constructor = self.yaml_constructors[node.tag]\n        else:\n            for tag_prefix in self.yaml_multi_constructors:\n                if node.tag.startswith(tag_prefix):\n                    tag_suffix = node.tag[len(tag_prefix):]\n                    constructor = self.yaml_multi_constructors[tag_prefix]\n                    break\n            else:\n                if None in self.yaml_multi_constructors:\n                    tag_suffix = node.tag\n                    constructor = self.yaml_multi_constructors[None]\n                elif None in self.yaml_constructors:\n                    constructor = self.yaml_constructors[None]","sourceCodeStart":43,"sourceCodeEnd":79,"githubUrl":"https://github.com/netdata/netdata/blob/4864de85e26f6734d92cfc27ccbeff5921f49938/src/collectors/python.d.plugin/python_modules/pyyaml3/constructor.py#L43-L79","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Remove the self-reference from the YAML — express shared data with ordinary aliases to a fully-defined node instead of a cyclic one.","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.","Check whether the recursion is accidental (an alias at the wrong indentation pointing at its own parent) and fix the alias target."],"exampleFix":"# before (cyclic -> unconstructable)\na: &x\n  b: *x\n\n# after (acyclic sharing)\nbase: &base {v: 1}\na:\n  b: *base","handlingStrategy":"try-catch","validationCode":"import yaml\n\ndef has_recursive_nodes(text):\n    def walk(node, seen):\n        if id(node) in seen:\n            return True\n        seen = seen | {id(node)}\n        if isinstance(node, yaml.MappingNode):\n            return any(walk(k, seen) or walk(v, seen) for k, v in node.value)\n        if isinstance(node, yaml.SequenceNode):\n            return any(walk(c, seen) for c in node.value)\n        return False\n    return walk(yaml.compose(text), frozenset())","typeGuard":null,"tryCatchPattern":"try:\n    cfg = yaml.safe_load(text)\nexcept yaml.ConstructorError as e:\n    if 'unconstructable recursive node' in str(e):\n        reject_config('cyclic YAML structures are not supported')\n    raise","preventionTips":["Keep config YAML strictly acyclic; model shared data with plain aliases to complete nodes.","Build cyclic object graphs in Python after loading, not in the YAML.","Reject self-referential snippets in config validation with a clear message."],"tags":["yaml","python","recursion","aliases"],"backgroundTag":null,"analyzedSha":"4864de85e26f6734d92cfc27ccbeff5921f49938","analyzedAt":"2026-08-15T09:12:38.226Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}