argoproj/argo-workflows · error
dependency cycle detected: %s->%s
Error message
dependency cycle detected: %s->%s
What it means
validateNoCycles performs a DFS over the template dependency graph (an adjacency list of template/step names to their dependencies). It returns this error when following dependencies leads back to a node already on the current path, i.e. the graph contains a cycle, which would make scheduling impossible.
Source
Thrown at pkg/apis/workflow/v1alpha1/validation_utils.go:90
}
// validateNoCycles validates that a dependency graph has no cycles by doing a Depth-First Search
// depGraph is an adjacency list, where key is a node name and value is a list of its dependencies' names
func validateNoCycles(depGraph map[string][]string) error {
visited := make(map[string]bool)
var noCyclesHelper func(currentName string, cycyle []string) error
noCyclesHelper = func(currentName string, cycle []string) error {
if _, ok := visited[currentName]; ok {
return nil
}
depNames, ok := depGraph[currentName]
if !ok {
return nil
}
for _, depName := range depNames {
for _, name := range cycle {
if depName == name {
return fmt.Errorf("dependency cycle detected: %s->%s", strings.Join(cycle, "->"), name)
}
}
cycle = append(cycle, depName)
err := noCyclesHelper(depName, cycle)
if err != nil {
return err
}
cycle = cycle[0 : len(cycle)-1]
}
visited[currentName] = true
return nil
}
names := make([]string, 0)
for name := range depGraph {
names = append(names, name)
}
// sort names here to make sure the error message has consistent ordering
// so that we can verify the error message in unit testsView on GitHub (pinned to 35bff19146)
Solutions
- Read the cycle from the error message (A->B->...->A) and remove or reverse one dependency edge
- Draw the DAG and check for the reported loop before adding new dependencies
- Refactor the cyclic group by splitting one template so the dependency is one-directional
Example fix
# before
A:
dependencies: [B]
B:
dependencies: [A]
# after
A: {}
B:
dependencies: [A] Defensive patterns
Strategy: validation
Validate before calling
func hasCycle(graph map[string][]string) bool {
var visit func(string, []string) bool
visit = func(n string, path []string) bool {
for _, p := range path {
if p == n { return true }
}
for _, d := range graph[n] {
if visit(d, append(path, n)) { return true }
}
return false
}
for n := range graph {
if visit(n, nil) { return true }
}
return false
} Type guard
null
Try / catch
if err := wf.Validate(); err != nil {
if strings.Contains(err.Error(), "dependency cycle detected") {
cycle := strings.TrimPrefix(err.Error(), "dependency cycle detected: ")
log.Printf("fix DAG cycle: %s", cycle)
}
return err
} Prevention
- Keep dependency edges strictly forward-ordered by design
- Validate DAGs in CI with argo lint
- Reject auto-generated edges that point backwards
When it happens
Trigger: Submitting or validating a Workflow whose templates' `dependencies` fields form a loop, e.g. A depends on B and B depends on A; triggered via Validate / DAG validation whenever noCyclesHelper finds a depName already present in the current cycle path.
Common situations: Hand-editing dependencies in a large DAG; auto-generated DAGs where an index off-by-one wraps around; adding a new edge that accidentally closes a loop.
Related errors
- duplicated nodeName %s
- invalid dependency %s
- task result '%s' for task '%s' is invalid
- cron schedules must consist of 5 values only
- ${e.Usage()}
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/08dd4128bd8e28cc.
Report an issue: GitHub.