mikefarah/yq · error
alias cycle detected
Error message
alias cycle detected
What it means
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.
Source
Thrown at pkg/yqlib/operator_traverse_path.go:48
if err != nil {
return Context{}, err
}
matches.PushBackList(newNodes)
}
return context.ChildContext(matches), nil
}
// resolveAliasChain follows an alias chain iteratively, returning the
// first non-alias node. Returns an error if a cycle is detected.
func resolveAliasChain(node *CandidateNode) (*CandidateNode, error) {
if node.Kind != AliasNode {
return node, nil
}
visited := map[*CandidateNode]bool{}
for node.Kind == AliasNode {
if visited[node] {
return nil, fmt.Errorf("alias cycle detected")
}
visited[node] = true
log.Debug("its an alias!")
node = node.Alias
}
return node, nil
}
func traverse(context Context, matchingNode *CandidateNode, operation *Operation) (*list.List, error) {
log.Debugf("Traversing %v", NodeToString(matchingNode))
var err error
matchingNode, err = resolveAliasChain(matchingNode)
if err != nil {
return nil, err
}
if matchingNode.Tag == "!!null" && operation.Value != "[]" && !context.DontAutoCreate {View on GitHub (pinned to 8b5af0694b)
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
Example fix
# before a: &x b: *x # after a: &x b: 1
Defensive patterns
Strategy: validation
Validate before calling
// Pre-check YAML for cyclic aliases before processing
func hasCyclicAliases(root *yaml.Node) bool {
visited := map[*yaml.Node]bool{}
var walk func(n *yaml.Node) bool
walk = func(n *yaml.Node) bool {
if n == nil || visited[n] { return n != nil && visited[n] }
visited[n] = true
if n.Kind == yaml.AliasNode { return visited[n.Alias] }
for _, c := range n.Content { if walk(c) { return true } }
return false
}
return walk(root)
} Type guard
func isAliasNode(n *yaml.Node) bool { return n != nil && n.Kind == yaml.AliasNode } Try / catch
out, err := yqEval(expr, doc)
if err != nil {
if strings.Contains(err.Error(), "alias cycle detected") {
// fall back to raw string processing or reject input
}
return err
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- yaml node has no content
- orderedMap: invalid yaml node
- INI encoder supports only MappingNode at the root level, got
- unsupported node kind for TOML: %v
- configure YAML encoding: %w
AI-assisted analysis of mikefarah/yq@8b5af0694b (2026-09-05).
Data as JSON: /api/errors/26a12c4191895f9b.
Report an issue: GitHub.