nektos/act · error

unresolved alias node

Error message

unresolved alias node

What it means

While inlining YAML aliases, resolveAliasesExt found an AliasNode whose .Alias pointer is nil — an alias node (*name) that does not point at any decoded anchor. Well-formed parsers never produce this, but truncated files, manual construction of yaml.Node trees, or pre-parsing that strips anchors can leave dangling alias nodes, which this guard rejects with 'unresolved alias node'.

Source

Thrown at pkg/model/anchors.go:17

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
			}
		}
	}
	return nil
}

View on GitHub (pinned to 4f41128141)

Solutions

  1. Search the workflow for every '*name' usage and add the missing '&name:' anchor or fix the typo.
  2. Replace the alias with a literal value if the anchor was intentionally removed.
  3. If you preprocess YAML, run the alias check on the final merged document, not fragments.
  4. Lint the file with any YAML validator to surface the undefined alias before running act.

Example fix

# before
jobs:
  build: &job_template
    runs-on: ubuntu-latest
  deploy: *job_templete   # typo -> unresolved alias node

# after
jobs:
  build: &job_template
    runs-on: ubuntu-latest
  deploy: *job_template
Defensive patterns

Strategy: validation

Validate before calling

# Verify every alias has a matching anchor
python3 - <<'EOF'
import re, sys
text = open(sys.argv[1]).read()
anchors = set(re.findall(r'&(\w+)', text))
aliases  = set(re.findall(r'\*(\w+)', text))
missing = aliases - anchors
if missing:
    sys.exit(f"unresolved aliases: {sorted(missing)}")
print("ok")
EOF

Try / catch

if err := model.NewSingleWorkflowPlanner(path)(...) ; err != nil {
    if strings.Contains(err.Error(), "unresolved alias node") {
        return fmt.Errorf("an alias (*name) has no matching anchor (&name) in the workflow: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: A workflow YAML containing '*foo' with no matching '&foo' anchor in the document; programmatically built or mutated yaml.Node trees where aliases were cloned without their targets; documents preprocessed by a tool that removed anchor definitions but kept the alias usages.

Common situations: Copy-pasting a snippet that uses an alias defined elsewhere in the original file; sed/script-based workflow refactors that delete the anchor line but not its references; partial merges of reusable workflow YAML; typos in alias names.

Related errors


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