argoproj/argo-workflows · error

invalid dependency %s

Error message

invalid dependency %s

What it means

During dependency registration, TopologicalSorting verifies each dependency string refers to a node already present in the graph (by NodeName). A dependency naming a node that was never added is rejected, since the sort cannot order against a nonexistent vertex.

Source

Thrown at util/sorting/topological_sorting.go:26

	NodeName     string
	Dependencies []string
}

func TopologicalSorting(graph []*TopologicalSortingNode) ([]*TopologicalSortingNode, error) {
	priorNodeCountMap := make(map[string]int, len(graph))               // nodeName -> priorNodeCount
	nextNodeMap := make(map[string][]string, len(graph))                // nodeName -> nextNodeList
	nodeNameMap := make(map[string]*TopologicalSortingNode, len(graph)) // nodeName -> node
	for _, node := range graph {
		if _, ok := nodeNameMap[node.NodeName]; ok {
			return nil, fmt.Errorf("duplicated nodeName %s", node.NodeName)
		}
		nodeNameMap[node.NodeName] = node
		priorNodeCountMap[node.NodeName] = len(node.Dependencies)
	}
	for _, node := range graph {
		for _, dependency := range node.Dependencies {
			if _, ok := nodeNameMap[dependency]; !ok {
				return nil, fmt.Errorf("invalid dependency %s", dependency)
			}
			nextNodeMap[dependency] = append(nextNodeMap[dependency], node.NodeName)
		}
	}

	queue := make([]*TopologicalSortingNode, len(graph))
	head, tail := 0, 0
	for nodeName, priorNodeCount := range priorNodeCountMap {
		if priorNodeCount == 0 {
			queue[tail] = nodeNameMap[nodeName]
			tail++
		}
	}

	for head < len(queue) {
		curr := queue[head]
		if curr == nil {
			return nil, fmt.Errorf("graph with cycle")

View on GitHub (pinned to 35bff19146)

Solutions

  1. Ensure every string in Dependencies matches exactly one node's NodeName in the same graph slice.
  2. When filtering nodes out of the graph, also remove/redirect their dependents' dependency references.
  3. Use the same name-building helper for both NodeName and Dependencies so they cannot diverge.
  4. Fix the test fixture to include the referenced dependency node.

Example fix

// before
graph = append(graph, &sorting.TopologicalSortingNode{NodeName: "B", Dependencies: []string{"A"}})
// after: node A must also be in the graph
graph = append(graph, &sorting.TopologicalSortingNode{NodeName: "A"},
    &sorting.TopologicalSortingNode{NodeName: "B", Dependencies: []string{"A"}})
Defensive patterns

Strategy: validation

Validate before calling

func allDepsResolve(graph []*sorting.TopologicalSortingNode) error {
    names := map[string]bool{}
    for _, n := range graph { names[n.NodeName] = true }
    for _, n := range graph {
        for _, d := range n.Dependencies {
            if !names[d] { return fmt.Errorf("dep %q missing from graph", d) }
        }
    }
    return nil
}

Try / catch

if err := allDepsResolve(graph); err != nil { return err }
sorted, err := sorting.TopologicalSorting(graph)
if err != nil { return fmt.Errorf("topological sort failed: %w", err) }

Prevention

When it happens

Trigger: A TopologicalSortingNode lists a Dependency whose name is not any node's NodeName — e.g. typos, dependency added for a node that was filtered out of the graph, or boundary/exit nodes referenced without being appended.

Common situations: Constructing the graph from DAG templates where an 'outbound' synchronization node is referenced but never inserted; renaming node names in one place but not in Dependencies; pruning failed steps from the graph while leaving their dependents' references; test fixtures with dangling dependencies.

Related errors


AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03). Data as JSON: /api/errors/4b6d4aef3f7315b7. Report an issue: GitHub.