nektos/act · error

circular alias

Error message

circular alias

What it means

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.

Source

Thrown at pkg/model/anchors.go:11

package model

import (
	"errors"

	"gopkg.in/yaml.v3"
)

func resolveAliasesExt(node *yaml.Node, path map[*yaml.Node]bool, skipCheck bool) error {
	if !skipCheck && path[node] {
		return errors.New("circular alias")
	}
	switch node.Kind {
	case yaml.AliasNode:
		aliasTarget := node.Alias
		if aliasTarget == nil {
			return errors.New("unresolved alias node")
		}
		path[node] = true
		*node = *aliasTarget
		if err := resolveAliasesExt(node, path, true); err != nil {
			return err
		}
		delete(path, node)

	case yaml.DocumentNode, yaml.MappingNode, yaml.SequenceNode:
		for _, child := range node.Content {
			if err := resolveAliasesExt(child, path, false); err != nil {
				return err

View on GitHub (pinned to 4f41128141)

Solutions

  1. Find the self-reference: search the workflow for repeated anchor/alias names and check whether the anchor's block contains '*same_name'.
  2. Break the cycle by duplicating the shared value or restructuring into two anchors where the second references the first only one way.
  3. If generated YAML, fix the generator so it does not emit recursive structures.
  4. 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).

Example fix

# before
env: &base
  A: 1
  B: *base   # circular alias

# after
base: &base
  A: 1
  B: 2
env: *base
Defensive patterns

Strategy: validation

Validate before calling

# Reject circular anchors before running act
python3 - <<'EOF'
import sys, yaml
class Loader(yaml.SafeLoader):
    pass
def no_alias_constructor(loader, node, deep=False):
    raise ValueError(f"circular/unsupported alias at line {node.start_mark.line}")
Loader.add_constructor(None, no_alias_constructor)  # strict mode catches cycles early
try:
    yaml.load(open(sys.argv[1]).read(), Loader=yaml.SafeLoader)
except yaml.constructor.ConstructorError as e:
    sys.exit(f"YAML problem: {e}")
print("ok")
EOF

Try / catch

planner, err := model.NewWorkflowPlanner(".github/workflows", true)
if err != nil {
    if strings.Contains(err.Error(), "circular alias") {
        return fmt.Errorf("workflow YAML has an anchor that references itself; break the cycle: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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 }'.

Related errors


AI-assisted analysis of nektos/act@4f41128141 (2026-08-15). Data as JSON: /api/errors/7c2303f0df01700f. Report an issue: GitHub.