{"record":{"id":"dae285f56d475aa7","repo":"gastownhall/beads","slug":"final-graph-cycle-check-w","errorCode":null,"errorMessage":"final graph cycle check: %w","messagePattern":"final graph cycle check: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/graph_apply.go","lineNumber":1086,"sourceCode":"\t\t\t\t\t}\n\t\t\t\t\tif (depType == types.DepParentChild) != parentPhase {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\td, err := types.NewGraphNodeDependency(issues[i].ID, depType, dep.Target, keyToID)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"node %q: %w\", node.Key, err)\n\t\t\t\t\t}\n\t\t\t\t\tif err := tx.AddDependency(ctx, d, actor); err != nil {\n\t\t\t\t\t\treturn fmt.Errorf(\"node %q: adding dep to %q: %w\", node.Key, dep.Target, err)\n\t\t\t\t\t}\n\t\t\t\t\tif graphApplySchedulingDependencyType(d.Type) {\n\t\t\t\t\t\tnewSchedulingEdges = append(newSchedulingEdges, [2]string{d.IssueID, d.DependsOnID})\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif cyclePath, err := tx.CycleThroughEdges(ctx, newSchedulingEdges); err != nil {\n\t\t\treturn fmt.Errorf(\"final graph cycle check: %w\", err)\n\t\t} else if cyclePath != \"\" {\n\t\t\treturn fmt.Errorf(\"graph dependency cycle would be created: %s\", cyclePath)\n\t\t}\n\n\t\t// Apply deferred assignees.\n\t\tfor i, assignee := range pendingAssignees {\n\t\t\tupdates := map[string]interface{}{\n\t\t\t\t\"assignee\": assignee,\n\t\t\t}\n\t\t\tif err := tx.UpdateIssue(ctx, issues[i].ID, updates, actor); err != nil {\n\t\t\t\treturn fmt.Errorf(\"node %q: setting assignee: %w\", plan.Nodes[i].Key, err)\n\t\t\t}\n\t\t}\n\n\t\treturn nil\n\t}); err != nil {\n\t\treturn nil, err\n\t}","sourceCodeStart":1068,"sourceCodeEnd":1104,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/graph_apply.go#L1068-L1104","documentation":"After all dependencies are written, graph apply runs tx.CycleThroughEdges over the newly added scheduling edges as a final safety net. This error wraps a failure of that check itself (the cycle detector errored), as opposed to a cycle actually being found — usually a storage/query problem while walking dependency edges.","triggerScenarios":"tx.CycleThroughEdges(ctx, newSchedulingEdges) returns a non-nil error after all deps were inserted, e.g. the storage backend failed to query the dependency graph.","commonSituations":"Storage connectivity drops mid-transaction, very large graphs exhausting query limits, or backend-specific query failures.","solutions":["Inspect the wrapped error for the storage-level cause and retry the apply","Check database health (bd doctor) and connectivity","Reduce plan size if the cycle query times out on very large graphs","Re-run after resolving any concurrent-writer conflicts"],"exampleFix":"// before: applying a 10k-node graph in one transaction times out\nbd graph apply huge-plan.json\n// after: split into smaller plans\nbd graph apply part1.json && bd graph apply part2.json","handlingStrategy":"retry","validationCode":"// no caller-side check can replace the in-transaction cycle query; ensure DB health first\nif err := db.Ping(); err != nil { return err }","typeGuard":null,"tryCatchPattern":"if err := bd.GraphApply(ctx, plan); err != nil {\n  if strings.Contains(err.Error(), \"final graph cycle check\") {\n    return retryWithBackoff(ctx, func() error { _, err := bd.GraphApply(ctx, plan); return err })\n  }\n  return err\n}","preventionTips":["Verify storage health (bd doctor) before very large applies","Split huge plans into smaller batches to reduce query load","Avoid flaky remote storage connections for apply operations"],"tags":["beads","graph-apply","cycle-detection","storage"],"backgroundTag":"cycle-check-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}