{"record":{"id":"7b53c54b93e287c6","repo":"gastownhall/beads","slug":"applygraph-node-q-planned-blocking-dependencies","errorCode":null,"errorMessage":"applyGraph: node %q: planned blocking dependencies create a path from parent %q to child %q","messagePattern":"applyGraph: node %q: planned blocking dependencies create a path from parent %q to child %q","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/issue.go","lineNumber":1433,"sourceCode":"\t\tadj[fromID] = append(adj[fromID], toID)\n\t}\n\n\tdepCache := make(map[string][]*types.Dependency)\n\tfor _, node := range plan.Nodes {\n\t\tparentID := node.ParentID\n\t\tif node.ParentKey != \"\" {\n\t\t\tparentID = keyToID[node.ParentKey]\n\t\t}\n\t\tchildID := keyToID[node.Key]\n\t\tif childID == \"\" || parentID == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\thasPath, err := u.graphHasPath(ctx, adj, depCache, parentID, childID, readyPathDepType)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif hasPath {\n\t\t\treturn fmt.Errorf(\"applyGraph: node %q: planned blocking dependencies create a path from parent %q to child %q\", node.Key, parentID, childID)\n\t\t}\n\t}\n\treturn nil\n}\n\n// validatePlannedBlockingCycles rejects planned blocking edges that would close\n// a blocking-dependency cycle, evaluated whole-graph before any insert. It\n// mirrors embedded validateGraphApplyPlannedBlockingCycles. This early\n// preflight is intentionally restricted to blocking edges; repository Insert\n// subsequently enforces the combined scheduling graph for every stored edge.\nfunc (u *issueUseCaseImpl) validatePlannedBlockingCycles(\n\tctx context.Context,\n\tplan GraphPlan,\n\tkeyToID map[string]string,\n) error {\n\ttype plannedEdge struct {\n\t\tindex  int\n\t\tfromID string","sourceCodeStart":1415,"sourceCodeEnd":1451,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/issue.go#L1415-L1451","documentation":"applyGraph's planned-blocking-path validation detected that the plan's blocking dependencies create a ready path from a parent issue to its own child — meaning the parent could be considered unblocked/ready only after its child, inverting the intended hierarchy ordering. The apply rejects this node's parent/child structure combined with the planned blocking edges.","triggerScenarios":"A node has a parent-child relationship (parentID -> childID) while the planned blocking edges (filtered to readyPathDepType) produce a directed path parent -> child, so the parent transitively depends on its own descendant.","commonSituations":"Adding a blocking edge from a parent to a subtask that the subtask's subtree blocks back; plans where parent/child roles were accidentally swapped; composing a subgraph plan onto an existing tree where cross edges close the path.","solutions":["Read the message to identify the node and the parent/child IDs on the offending path.","Remove or reverse the blocking edge that links parent back toward child.","Restructure the plan so parents never depend (transitively) on their children.","Re-run apply after the hierarchy and blocking edges are consistent."],"exampleFix":"// before: parent blocked by its own child\nnodes:\n  - key: parent\n    deps: [{target: child, type: blocks}]\n  - key: child\n    parent: parent\n// after: child blocks on parent context, not the reverse\nnodes:\n  - key: parent\n  - key: child\n    parent: parent\n    deps: [{target: parent, type: blocks}]","handlingStrategy":"validation","validationCode":"// Pre-check: no blocking path may go from any parent to its child\nfor _, n := range plan.Nodes {\n    if n.Parent == \"\" { continue }\n    if reachable(adj(plan.Edges, readyPathDepType), n.Key /*parent*/, n.Parent /*child*/) {\n        return fmt.Errorf(\"node %s: blocking path parent->child\", n.Key)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := bd.GraphApply(ctx, plan); err != nil {\n    if strings.Contains(err.Error(), \"path from parent\") {\n        return fmt.Errorf(\"restructure hierarchy: %w\", err)\n    }\n    return err\n}","preventionTips":["Never add blocking edges whose direction points from a parent back into its subtree","Draw the hierarchy and edge directions before authoring plans","Validate parent/child orientation in generated plans"],"tags":["go","dependency-cycle","hierarchy","validation"],"backgroundTag":"parent-child-blocking-path","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}