gastownhall/beads · error
edge %d %s->%s duplicates a parent-child relationship with d
Error message
edge %d %s->%s duplicates a parent-child relationship with dependency type %q
What it means
The plan contains an explicit parent-child dependency (via node parents) and an additional edge over the same pair with a different dependency type. Since the pair is already parent-child, any other dep type would duplicate or contradict it, so validation rejects the edge during apply.
Source
Thrown at cmd/bd/graph_apply.go:1002
if err := tx.UpdateIssue(ctx, issues[i].ID, updates, actor); err != nil {
return fmt.Errorf("node %q: updating metadata refs: %w", node.Key, err)
}
}
parentDepPairs := graphApplyParentDepPairs(plan.Nodes, keyToID)
newSchedulingEdges := make([][2]string, 0, len(plan.Nodes)+len(plan.Edges))
if err := validateGraphApplyPlannedParentBlockingPaths(ctx, tx, plan, keyToID, parentDepPairs); err != nil {
return err
}
if err := validateGraphApplyPlannedBlockingCycles(ctx, tx, plan, keyToID); err != nil {
return err
}
for i, edge := range plan.Edges {
fromID := resolveEdgeRef(edge.FromKey, edge.FromID, keyToID)
toID := resolveEdgeRef(edge.ToKey, edge.ToID, keyToID)
depType := graphApplyDependencyType(edge.Type)
if parentDepPairs[graphApplyDepPairKey(fromID, toID)] && depType != types.DepParentChild {
return fmt.Errorf("edge %d %s->%s duplicates a parent-child relationship with dependency type %q", i, fromID, toID, depType)
}
if parentDepPairs[graphApplyDepPairKey(toID, fromID)] && graphApplyCycleRelevantDependencyType(depType) {
return fmt.Errorf("edge %d %s->%s creates a blocking reverse of a parent-child relationship", i, fromID, toID)
}
}
// Add node parent-child dependencies first. The explicit and inline
// dependency sources below are also processed parent-first, so every
// blocking edge sees the plan's full hierarchy in storage.
for i, node := range plan.Nodes {
parentKey := node.effectiveParentKey()
parentID := node.ParentID
if parentKey != "" {
parentID = keyToID[parentKey]
}
if parentID != "" {
dep := &types.Dependency{
IssueID: issues[i].ID,View on GitHub (pinned to 71377f2769)
Solutions
- Remove the redundant edge; the parent-child dependency already covers it.
- Change edge.Type to the parent-child type if that relationship is intended.
- Fix the plan generator to skip edges duplicating inline parent relationships.
Example fix
// before
node: {"key": "bd-1", "parents": ["bd-2"]}
edge: {"from": "bd-1", "to": "bd-2", "type": "blocks"}
// after
node: {"key": "bd-1", "parents": ["bd-2"]} Defensive patterns
Strategy: validation
Validate before calling
pairs := map[[2]string]bool{}
for _, n := range plan.Nodes {
for _, p := range n.Parents { pairs[[2]string{n.Key, p}] = true }
}
for _, e := range plan.Edges {
if pairs[[2]string{e.From, e.To}] && e.Type != "parent-child" {
return fmt.Errorf("edge %s->%s duplicates a parent-child pair", e.From, e.To)
}
} Type guard
func duplicatesParentChild(e Edge, pairs map[[2]string]bool) bool {
return pairs[[2]string{e.From, e.To}] && e.Type != "parent-child"
} Try / catch
if err := bd.GraphApply(ctx, plan); err != nil {
if strings.Contains(err.Error(), "duplicates a parent-child relationship") {
plan = dropRedundantEdges(plan); return bd.GraphApply(ctx, plan)
}
} Prevention
- Deduplicate edges against inline parent relationships in generators.
- Never emit a second edge type over a pair already declared parent-child.
- Lint plans for repeated node pairs.
When it happens
Trigger: plan.Edges includes an edge fromID->toID where the pair was already established as parent-child (present in parentDepPairs) but edge.Type maps to something other than DepParentChild.
Common situations: Generated plans that emit both inline parents and a separate generic edge for the same pair; merging two plans that model the same relationship differently; hand-edited edges with a wrong type field.
Related errors
- edge %d %s->%s creates a blocking reverse of a parent-child
- --deps cannot attach both %q and %q to the same target %q: a
- --deps target is empty
- invalid dependency type %q (must be non-empty, max %d chars)
- unknown dependency type %q; valid types: %s
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/f972e3cf4adb215d.
Report an issue: GitHub.