{"record":{"id":"f972e3cf4adb215d","repo":"gastownhall/beads","slug":"edge-d-s-s-duplicates-a-parent-child-relations","errorCode":null,"errorMessage":"edge %d %s->%s duplicates a parent-child relationship with dependency type %q","messagePattern":"edge (.+?) (.+?)->(.+?) duplicates a parent-child relationship with dependency type %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/graph_apply.go","lineNumber":1002,"sourceCode":"\t\t\tif err := tx.UpdateIssue(ctx, issues[i].ID, updates, actor); err != nil {\n\t\t\t\treturn fmt.Errorf(\"node %q: updating metadata refs: %w\", node.Key, err)\n\t\t\t}\n\t\t}\n\n\t\tparentDepPairs := graphApplyParentDepPairs(plan.Nodes, keyToID)\n\t\tnewSchedulingEdges := make([][2]string, 0, len(plan.Nodes)+len(plan.Edges))\n\t\tif err := validateGraphApplyPlannedParentBlockingPaths(ctx, tx, plan, keyToID, parentDepPairs); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := validateGraphApplyPlannedBlockingCycles(ctx, tx, plan, keyToID); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfor i, edge := range plan.Edges {\n\t\t\tfromID := resolveEdgeRef(edge.FromKey, edge.FromID, keyToID)\n\t\t\ttoID := resolveEdgeRef(edge.ToKey, edge.ToID, keyToID)\n\t\t\tdepType := graphApplyDependencyType(edge.Type)\n\t\t\tif parentDepPairs[graphApplyDepPairKey(fromID, toID)] && depType != types.DepParentChild {\n\t\t\t\treturn fmt.Errorf(\"edge %d %s->%s duplicates a parent-child relationship with dependency type %q\", i, fromID, toID, depType)\n\t\t\t}\n\t\t\tif parentDepPairs[graphApplyDepPairKey(toID, fromID)] && graphApplyCycleRelevantDependencyType(depType) {\n\t\t\t\treturn fmt.Errorf(\"edge %d %s->%s creates a blocking reverse of a parent-child relationship\", i, fromID, toID)\n\t\t\t}\n\t\t}\n\n\t\t// Add node parent-child dependencies first. The explicit and inline\n\t\t// dependency sources below are also processed parent-first, so every\n\t\t// blocking edge sees the plan's full hierarchy in storage.\n\t\tfor i, node := range plan.Nodes {\n\t\t\tparentKey := node.effectiveParentKey()\n\t\t\tparentID := node.ParentID\n\t\t\tif parentKey != \"\" {\n\t\t\t\tparentID = keyToID[parentKey]\n\t\t\t}\n\t\t\tif parentID != \"\" {\n\t\t\t\tdep := &types.Dependency{\n\t\t\t\t\tIssueID:     issues[i].ID,","sourceCodeStart":984,"sourceCodeEnd":1020,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/graph_apply.go#L984-L1020","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nnode: {\"key\": \"bd-1\", \"parents\": [\"bd-2\"]}\nedge: {\"from\": \"bd-1\", \"to\": \"bd-2\", \"type\": \"blocks\"}\n// after\nnode: {\"key\": \"bd-1\", \"parents\": [\"bd-2\"]}","handlingStrategy":"validation","validationCode":"pairs := map[[2]string]bool{}\nfor _, n := range plan.Nodes {\n  for _, p := range n.Parents { pairs[[2]string{n.Key, p}] = true }\n}\nfor _, e := range plan.Edges {\n  if pairs[[2]string{e.From, e.To}] && e.Type != \"parent-child\" {\n    return fmt.Errorf(\"edge %s->%s duplicates a parent-child pair\", e.From, e.To)\n  }\n}","typeGuard":"func duplicatesParentChild(e Edge, pairs map[[2]string]bool) bool {\n  return pairs[[2]string{e.From, e.To}] && e.Type != \"parent-child\"\n}","tryCatchPattern":"if err := bd.GraphApply(ctx, plan); err != nil {\n  if strings.Contains(err.Error(), \"duplicates a parent-child relationship\") {\n    plan = dropRedundantEdges(plan); return bd.GraphApply(ctx, plan)\n  }\n}","preventionTips":["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."],"tags":["cli","graph-apply","edges","dependencies","validation"],"backgroundTag":"duplicate-dependency-edge","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}