{"record":{"id":"afb7eb642ac9a15e","repo":"gastownhall/beads","slug":"applygraph-edge-d-s-s-creates-a-blocking-depe","errorCode":null,"errorMessage":"applyGraph: edge %d %s->%s creates a blocking dependency cycle","messagePattern":"applyGraph: edge (.+?) (.+?)->(.+?) creates a blocking dependency cycle","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/issue.go","lineNumber":1471,"sourceCode":"\t}\n\n\tadj := make(map[string][]string)\n\tchecks := make([]plannedEdge, 0, len(plan.Edges))\n\tfor i, edge := range plan.Edges {\n\t\tdepType := edge.Type\n\t\tif depType == \"\" {\n\t\t\tdepType = types.DepBlocks\n\t\t}\n\t\tif !cycleRelevantDepType(depType) {\n\t\t\tcontinue\n\t\t}\n\t\tfromID := resolveEdgeRef(edge.FromKey, edge.FromID, keyToID)\n\t\ttoID := resolveEdgeRef(edge.ToKey, edge.ToID, keyToID)\n\t\tif fromID == \"\" || toID == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif fromID == toID {\n\t\t\treturn fmt.Errorf(\"applyGraph: edge %d %s->%s creates a blocking dependency cycle\", i, fromID, toID)\n\t\t}\n\t\tadj[fromID] = append(adj[fromID], toID)\n\t\tchecks = append(checks, plannedEdge{index: i, fromID: fromID, toID: toID})\n\t}\n\n\tdepCache := make(map[string][]*types.Dependency)\n\tfor _, edge := range checks {\n\t\thasPath, err := u.graphHasPath(ctx, adj, depCache, edge.toID, edge.fromID, cycleRelevantDepType)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"applyGraph: edge %d %s->%s: checking planned blocking cycle: %w\", edge.index, edge.fromID, edge.toID, err)\n\t\t}\n\t\tif hasPath {\n\t\t\treturn fmt.Errorf(\"applyGraph: edge %d %s->%s creates a blocking dependency cycle\", edge.index, edge.fromID, edge.toID)\n\t\t}\n\t}\n\treturn nil\n}\n","sourceCodeStart":1453,"sourceCodeEnd":1489,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/issue.go#L1453-L1489","documentation":"During planned blocking-cycle validation, an edge whose resolved fromID equals its toID (a self-loop) is immediately rejected as a blocking dependency cycle — an issue cannot block itself. The message reports the edge index and the resolved issue ID on both sides.","triggerScenarios":"A plan edge where fromKey/fromID and toKey/toID resolve to the same issue — e.g. both set to the same node's key, duplicated keys, or resolveEdgeRef mapping two different key spellings to one ID.","commonSituations":"Copy-paste errors in plan files where from and to are identical; templated plan generation emitting the same key on both sides; aliasing where two keys map to one issue ID after creation.","solutions":["Find edge <index> in the plan and check whether from and to resolve to the same node.","Correct the edge so it connects two distinct issues, or delete it.","Ensure node keys are unique and no aliasing collapses two keys to one ID.","Re-run apply."],"exampleFix":"// before: self edge\nedges:\n  - {from: bd-1, to: bd-1, type: blocks}\n// after: distinct endpoints or removed\nedges: []","handlingStrategy":"validation","validationCode":"// Reject self-edges before apply\nfor i, e := range plan.Edges {\n    if resolveEdgeRef(e.FromKey, e.FromID, keyToID) == resolveEdgeRef(e.ToKey, e.ToID, keyToID) {\n        return fmt.Errorf(\"edge %d is a self-loop\", i)\n    }\n}","typeGuard":"func isSelfEdge(e Edge, keyToID map[string]string) bool {\n    return resolveEdgeRef(e.FromKey, e.FromID, keyToID) != \"\" &&\n        resolveEdgeRef(e.FromKey, e.FromID, keyToID) == resolveEdgeRef(e.ToKey, e.ToID, keyToID)\n}","tryCatchPattern":"if err := bd.GraphApply(ctx, plan); err != nil {\n    if strings.Contains(err.Error(), \"creates a blocking dependency cycle\") {\n        return fmt.Errorf(\"fix plan edges (self-loop or cycle): %w\", err)\n    }\n    return err\n}","preventionTips":["Check from != to on every edge in plan linters","Ensure node keys are unique so aliasing cannot collapse endpoints","Filter no-op edges out of generated plans"],"tags":["go","dependency-cycle","self-loop","validation"],"backgroundTag":"dependency-cycle-detected","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}