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
- Deduplicate the graph slice before calling TopologicalSorting (map by NodeName).
- Make node names unique by qualifying them with their parent (e.g. group/template/step identifiers) as the controller does.
- Fix loops that re-append the same node on retries; reuse the existing entry instead.
- 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
- Build node names through one qualified-name helper (parent/child) so collisions cannot occur.
- Deduplicate by NodeName right before sorting as an invariant check.
- Avoid re-appending nodes in retry/patch code paths; update in place.
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
- invalid dependency %s
- graph with cycle
- successCondition, failureCondition and outputs are not suppo
- duration has to be positive, current duration: %v
- containers must have at least one container
AI-assisted analysis of argoproj/argo-workflows@35bff19146 (2026-09-03).
Data as JSON: /api/errors/36a461ce793af150.
Report an issue: GitHub.