{"record":{"id":"e3860a9a19b6f9f1","repo":"gastownhall/beads","slug":"applygraph-dependency-cycle-would-be-created-s","errorCode":null,"errorMessage":"applyGraph: dependency cycle would be created: %s","messagePattern":"applyGraph: dependency cycle would be created: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/issue.go","lineNumber":1294,"sourceCode":"\t\t\t\tif err != nil {\n\t\t\t\t\treturn GraphApplyResult{}, fmt.Errorf(\"applyGraph: node %q: %w\", node.Key, err)\n\t\t\t\t}\n\t\t\t\tif (dep.Type == types.DepParentChild) != parentPhase {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif err := u.depRepo.Insert(ctx, dep, actor, DepInsertOpts{UseWispsTable: useWisp}); err != nil {\n\t\t\t\t\treturn GraphApplyResult{}, fmt.Errorf(\"applyGraph: node %q: adding dep to %q: %w\", node.Key, nd.Target, err)\n\t\t\t\t}\n\t\t\t\tif types.IsSchedulingEdge(dep.Type) {\n\t\t\t\t\tnewSchedulingEdges = append(newSchedulingEdges, [2]string{dep.IssueID, dep.DependsOnID})\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif cyclePath, err := u.depRepo.CycleThroughEdges(ctx, newSchedulingEdges); err != nil {\n\t\treturn GraphApplyResult{}, fmt.Errorf(\"applyGraph: final cycle check: %w\", err)\n\t} else if cyclePath != \"\" {\n\t\treturn GraphApplyResult{}, fmt.Errorf(\"applyGraph: dependency cycle would be created: %s\", cyclePath)\n\t}\n\n\t// Pass 5 — apply deferred assignees.\n\tfor i, assignee := range pendingAssignees {\n\t\tif assignee == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tid := keyToID[plan.Nodes[i].Key]\n\t\tif err := u.issueRepo.Update(ctx, id, map[string]any{\"assignee\": assignee}, actor, IssueTableOpts{UseWispsTable: useWisp}); err != nil {\n\t\t\treturn GraphApplyResult{}, fmt.Errorf(\"applyGraph: node %q: defer assignee: %w\", plan.Nodes[i].Key, err)\n\t\t}\n\t}\n\n\treturn GraphApplyResult{IDs: keyToID}, nil\n}\n\n// graphParentDepPairs encodes the (childID, parentID) parent-child pairs\n// implied by the plan's node ParentKey/ParentID fields. Used by applyGraph","sourceCodeStart":1276,"sourceCodeEnd":1312,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/issue.go#L1276-L1312","documentation":"The final cycle check found that the edges in this plan would create a dependency cycle among scheduling edges; applyGraph refuses to apply and returns the cycle path in the message. This is an intentional validation failure, not an internal error — the plan itself is cyclic.","triggerScenarios":"A plan whose blocking/scheduling edges form a loop, e.g. A blocks B, B blocks C, C blocks A (possibly combined with pre-existing edges in the DB closing a loop that only becomes cyclic with the new edges).","commonSituations":"Hand-authored or generated plans where blocking direction was inverted; merging plans from multiple sources that each look acyclic but compose into a cycle; an existing dependency in the DB plus one new edge closing the loop.","solutions":["Read the cycle path printed in the message to identify the offending edge loop.","Remove or reverse one edge in the cycle in the plan file.","Check existing DB dependencies — the cycle may need only one new edge removed to break.","Re-run bd graph apply after breaking the loop."],"exampleFix":"// before: cyclic plan\nedges:\n  - {from: a, to: b, type: blocks}\n  - {from: b, to: c, type: blocks}\n  - {from: c, to: a, type: blocks}\n// after: break the cycle by dropping/reversing one edge\nedges:\n  - {from: a, to: b, type: blocks}\n  - {from: b, to: c, type: blocks}","handlingStrategy":"validation","validationCode":"// Detect scheduling-edge cycles in the plan before apply\nfunc hasSchedulingCycle(edges []Edge) bool {\n    adj := map[string][]string{}\n    for _, e := range edges { adj[e.From] = append(adj[e.From], e.To) }\n    return detectCycleDFS(adj) != \"\"\n}","typeGuard":null,"tryCatchPattern":"if err := bd.GraphApply(ctx, plan); err != nil {\n    if cycle, ok := parseCyclePath(err); ok {\n        fmt.Printf(\"remove one edge from this loop: %s\\n\", cycle)\n        return errPlanCyclic\n    }\n    return err\n}","preventionTips":["Run a DFS cycle check over plan edges before applying","Include existing DB dependencies in the pre-check, not just plan edges","Never author mutual blocking pairs (a blocks b AND b blocks a)"],"tags":["go","dependency-cycle","validation","graph"],"backgroundTag":"dependency-cycle-detected","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}