hashicorp/packer · error

Cycle: %s

Error message

Cycle: %s

What it means

AcyclicGraph.Validate detects strongly-connected components in the DAG; any component with more than one vertex is a dependency cycle. For each cycle it reports `Cycle: <v1, v2, ...>` and joins the errors, so callers like buildPrereqsDAG can see every cycle at once. A cycle means the dependency graph cannot be topologically ordered.

Source

Thrown at internal/dag/dag.go:42

type DepthWalkFunc func(Vertex, int) error

func (g *AcyclicGraph) DirectedGraph() Grapher {
	return g
}

// Validate validates the DAG. A DAG is valid if it has no cycles or self-referencing vertex.
func (g *AcyclicGraph) Validate() error {
	// Look for cycles of more than 1 component
	var err error
	cycles := g.Cycles()
	if len(cycles) > 0 {
		for _, cycle := range cycles {
			cycleStr := make([]string, len(cycle))
			for j, vertex := range cycle {
				cycleStr[j] = VertexName(vertex)
			}

			err = errors.Join(err, fmt.Errorf(
				"Cycle: %s", strings.Join(cycleStr, ", ")))
		}
	}

	// Look for cycles to self
	for _, e := range g.Edges() {
		if e.Source() == e.Target() {
			err = errors.Join(err, fmt.Errorf(
				"Self reference: %s", VertexName(e.Source())))
		}
	}

	return err
}

// Cycles reports any cycles between graph nodes.
// Self-referencing nodes are not reported, and must be detected separately.
func (g *AcyclicGraph) Cycles() [][]Vertex {

View on GitHub (pinned to eb36e3c3e4)

Solutions

  1. Remove the edge that closes the loop — check the named vertices and break the mutual dependency
  2. Use a multi-step build or artifact dependency (post-processor/builder output) instead of a direct prereq cycle
  3. Trace the reported cycle list vertex-by-vertex to find the config entry that introduces the back-edge
  4. If building the graph programmatically, add a guard that rejects an edge when a reverse-reachable path exists

Example fix

// before
graph.Add(a, b)
graph.Add(b, a) // Cycle: a, b
// after
graph.Add(a, b) // single direction only
Defensive patterns

Strategy: validation

Validate before calling

// Detect cycles before Validate
func hasCycle(g *dag.AcyclicGraph) bool {
    var cycles []verticies
    // strongly-connected-component check, or simply:
    return len(g.Cycles()) > 0
}

Try / catch

if err := g.Validate(); err != nil {
    if strings.Contains(err.Error(), "Cycle:") {
        return fmt.Errorf("prerequisite graph has a dependency cycle: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: buildPrereqsDAG adds edges A→B and B→A (or longer loops) between build prerequisites; Validate enumerates the SCC and emits this error naming the vertices in the cycle.

Common situations: Two components list each other as prerequisites; typo causing component A's prereq to resolve back to A through a chain; programmatically generated graphs adding both directions of an edge; HCL configs with mutually dependent build targets.

Related errors


AI-assisted analysis of hashicorp/packer@eb36e3c3e4 (2026-09-05). Data as JSON: /api/errors/e8cd9bbf90e7182d. Report an issue: GitHub.