{"record":{"id":"26a12c4191895f9b","repo":"mikefarah/yq","slug":"alias-cycle-detected","errorCode":null,"errorMessage":"alias cycle detected","messagePattern":"alias cycle detected","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/yqlib/operator_traverse_path.go","lineNumber":48,"sourceCode":"\t\tif err != nil {\n\t\t\treturn Context{}, err\n\t\t}\n\t\tmatches.PushBackList(newNodes)\n\t}\n\n\treturn context.ChildContext(matches), nil\n}\n\n// resolveAliasChain follows an alias chain iteratively, returning the\n// first non-alias node. Returns an error if a cycle is detected.\nfunc resolveAliasChain(node *CandidateNode) (*CandidateNode, error) {\n\tif node.Kind != AliasNode {\n\t\treturn node, nil\n\t}\n\tvisited := map[*CandidateNode]bool{}\n\tfor node.Kind == AliasNode {\n\t\tif visited[node] {\n\t\t\treturn nil, fmt.Errorf(\"alias cycle detected\")\n\t\t}\n\t\tvisited[node] = true\n\t\tlog.Debug(\"its an alias!\")\n\t\tnode = node.Alias\n\t}\n\treturn node, nil\n}\n\nfunc traverse(context Context, matchingNode *CandidateNode, operation *Operation) (*list.List, error) {\n\tlog.Debugf(\"Traversing %v\", NodeToString(matchingNode))\n\n\tvar err error\n\tmatchingNode, err = resolveAliasChain(matchingNode)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif matchingNode.Tag == \"!!null\" && operation.Value != \"[]\" && !context.DontAutoCreate {","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/mikefarah/yq/blob/8b5af0694bb82b41d4ae180fac9972029066f90a/pkg/yqlib/operator_traverse_path.go#L30-L66","documentation":"yq resolves YAML anchors and aliases by following the alias node's Alias pointer to the real node. If following that chain returns to an alias node already visited, resolution would loop forever, so yq aborts with 'alias cycle detected'. This guards against self-referential or mutually recursive anchors in the input document.","triggerScenarios":"Traversing a path expression (e.g. `yq '.a.b' file.yaml`) against a document whose aliases form a cycle: an anchor whose value aliases itself, or two anchors that alias each other.","commonSituations":"Hand-edited or generated YAML with recursive anchors (e.g. copy-pasted Kubernetes/Compose anchors), templating tools that emit cyclic references, or programmatically built YAML via go-yaml where nodes were wired back onto themselves.","solutions":["Inspect the input YAML and break the alias cycle so each alias points to a non-cyclic anchor","Replace the recursive alias with an explicit duplicated value or use x-expand-anchors style expansion before processing","Check that your YAML serializer (e.g. go-yaml custom marshalling) is not re-using nodes recursively","Validate the document with a YAML linter that detects cyclic anchors before running yq"],"exampleFix":"# before\na: &x\n  b: *x\n# after\na: &x\n  b: 1\n","handlingStrategy":"validation","validationCode":"// Pre-check YAML for cyclic aliases before processing\nfunc hasCyclicAliases(root *yaml.Node) bool {\n\tvisited := map[*yaml.Node]bool{}\n\tvar walk func(n *yaml.Node) bool\n\twalk = func(n *yaml.Node) bool {\n\t\tif n == nil || visited[n] { return n != nil && visited[n] }\n\t\tvisited[n] = true\n\t\tif n.Kind == yaml.AliasNode { return visited[n.Alias] }\n\t\tfor _, c := range n.Content { if walk(c) { return true } }\n\t\treturn false\n\t}\n\treturn walk(root)\n}","typeGuard":"func isAliasNode(n *yaml.Node) bool { return n != nil && n.Kind == yaml.AliasNode }","tryCatchPattern":"out, err := yqEval(expr, doc)\nif err != nil {\n\tif strings.Contains(err.Error(), \"alias cycle detected\") {\n\t\t// fall back to raw string processing or reject input\n\t}\n\treturn err\n}","preventionTips":["Never create anchors whose value references themselves","Run a YAML linter that detects cyclic anchors in CI","When generating YAML programmatically, track node identity to avoid recursive reuse","Prefer explicit values over deep alias chains in generated configs"],"tags":["yaml","alias","anchors","recursion"],"backgroundTag":"yaml-alias-cycle","analyzedSha":"8b5af0694bb82b41d4ae180fac9972029066f90a","analyzedAt":"2026-09-05T10:57:22.766Z","contentChangedAt":"2026-09-05T10:57:22.766Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}