argoproj/argo-workflows · error

duplicated nodeName %s

Error message

duplicated nodeName %s

What it means

TopologicalSorting builds a DAG from TopologicalSortingNode entries keyed by NodeName; duplicate NodeNames would corrupt the indegree/adjacency maps, so the algorithm rejects them immediately. Each node in the graph must be uniquely named before sorting.

Source

Thrown at util/sorting/topological_sorting.go:18

package sorting

import (
	"fmt"
)

type TopologicalSortingNode struct {
	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]

View on GitHub (pinned to 35bff19146)

Solutions

  1. Deduplicate the graph slice before calling TopologicalSorting (map by NodeName).
  2. Make node names unique by qualifying them with their parent (e.g. group/template/step identifiers) as the controller does.
  3. Fix loops that re-append the same node on retries; reuse the existing entry instead.
  4. Fix the fixture in tests so each node appears once.

Example fix

// before
graph = append(graph, node)
graph = append(graph, node) // duplicate
// after
seen := map[string]bool{}
if !seen[node.NodeName] {
    graph = append(graph, node)
    seen[node.NodeName] = true
}
Defensive patterns

Strategy: validation

Validate before calling

func hasUniqueNames(graph []*sorting.TopologicalSortingNode) bool {
    seen := map[string]bool{}
    for _, n := range graph {
        if seen[n.NodeName] { return false }
        seen[n.NodeName] = true
    }
    return true
}

Try / catch

sorted, err := sorting.TopologicalSorting(graph)
if err != nil {
    if strings.HasPrefix(err.Error(), "duplicated nodeName") {
        graph = dedupeByNodeName(graph)
        sorted, err = sorting.TopologicalSorting(graph)
    }
    if err != nil { return err }
}

Prevention

When it happens

Trigger: Passing a []*TopologicalSortingNode where two entries share the same NodeName (e.g. appending a node twice, or building the list per-template without including the node/step index in the name).

Common situations: DAG synchronization code appending boundary nodes that already exist; retry logic re-adding an entry for the same node; constructing dependencies for broadened nodes but reusing names across steps; unit-test fixtures that duplicate a node.

Related errors


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