{"record":{"id":"7c2303f0df01700f","repo":"nektos/act","slug":"circular-alias","errorCode":null,"errorMessage":"circular alias","messagePattern":"circular alias","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/model/anchors.go","lineNumber":11,"sourceCode":"package model\n\nimport (\n\t\"errors\"\n\n\t\"gopkg.in/yaml.v3\"\n)\n\nfunc resolveAliasesExt(node *yaml.Node, path map[*yaml.Node]bool, skipCheck bool) error {\n\tif !skipCheck && path[node] {\n\t\treturn errors.New(\"circular alias\")\n\t}\n\tswitch node.Kind {\n\tcase yaml.AliasNode:\n\t\taliasTarget := node.Alias\n\t\tif aliasTarget == nil {\n\t\t\treturn errors.New(\"unresolved alias node\")\n\t\t}\n\t\tpath[node] = true\n\t\t*node = *aliasTarget\n\t\tif err := resolveAliasesExt(node, path, true); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdelete(path, node)\n\n\tcase yaml.DocumentNode, yaml.MappingNode, yaml.SequenceNode:\n\t\tfor _, child := range node.Content {\n\t\t\tif err := resolveAliasesExt(child, path, false); err != nil {\n\t\t\t\treturn err","sourceCodeStart":1,"sourceCodeEnd":29,"githubUrl":"https://github.com/nektos/act/blob/4f411281417e88660bea1c1a1749aa71ae0bd60f/pkg/model/anchors.go#L1-L29","documentation":"resolveAliasesExt walks the parsed YAML tree inlining alias nodes into their anchors; the path map tracks nodes currently being expanded so that an alias chain that re-enters itself is detected. When a node is encountered that is already on the expansion path, the YAML contains a cycle (an anchor that, directly or through nesting, references its own alias), and 'circular alias' is returned, aborting workflow parsing.","triggerScenarios":"Hand-crafted or machine-generated YAML where an anchor's value contains an alias back to that same anchor, e.g. 'a: &x {b: *x}', or a longer mutual chain across mapping/sequence nodes. YAML serializers that emit self-referential structures produce this on re-parse.","commonSituations":"Matrix or env blocks refactored with YAML anchors where a copy accidentally references the original; templating tools (yq, Jinja emitting YAML) that generate self-referential merges; attempting recursive defaults like 'defaults: &d { inherit: *d }'.","solutions":["Find the self-reference: search the workflow for repeated anchor/alias names and check whether the anchor's block contains '*same_name'.","Break the cycle by duplicating the shared value or restructuring into two anchors where the second references the first only one way.","If generated YAML, fix the generator so it does not emit recursive structures.","Validate the file with a strict YAML linter or 'python -c \"import yaml,sys; yaml.safe_load(open(sys.argv[1]))\"' to confirm the cycle is gone (safe_load rejects recursive aliases by default)."],"exampleFix":"# before\nenv: &base\n  A: 1\n  B: *base   # circular alias\n\n# after\nbase: &base\n  A: 1\n  B: 2\nenv: *base","handlingStrategy":"validation","validationCode":"# Reject circular anchors before running act\npython3 - <<'EOF'\nimport sys, yaml\nclass Loader(yaml.SafeLoader):\n    pass\ndef no_alias_constructor(loader, node, deep=False):\n    raise ValueError(f\"circular/unsupported alias at line {node.start_mark.line}\")\nLoader.add_constructor(None, no_alias_constructor)  # strict mode catches cycles early\ntry:\n    yaml.load(open(sys.argv[1]).read(), Loader=yaml.SafeLoader)\nexcept yaml.constructor.ConstructorError as e:\n    sys.exit(f\"YAML problem: {e}\")\nprint(\"ok\")\nEOF","typeGuard":null,"tryCatchPattern":"planner, err := model.NewWorkflowPlanner(\".github/workflows\", true)\nif err != nil {\n    if strings.Contains(err.Error(), \"circular alias\") {\n        return fmt.Errorf(\"workflow YAML has an anchor that references itself; break the cycle: %w\", err)\n    }\n    return err\n}","preventionTips":["Avoid anchors whose value aliases themselves, even indirectly through nested nodes.","Run any strict YAML validator on workflow files in CI.","When generating YAML, avoid emitting recursive data structures."],"tags":["yaml","anchors","aliases","workflow-parsing"],"backgroundTag":null,"analyzedSha":"4f411281417e88660bea1c1a1749aa71ae0bd60f","analyzedAt":"2026-08-15T09:19:46.307Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}