{"record":{"id":"6593a3e6ea8869e5","repo":"gastownhall/beads","slug":"dependency-graph-rows-s-w","errorCode":null,"errorMessage":"dependency graph: rows %s: %w","messagePattern":"dependency graph: rows (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/cycles.go","lineNumber":117,"sourceCode":"\t\t\tFROM %s\n\t\t`, DepTargetExpr, depTable))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"dependency graph: query %s: %w\", depTable, err)\n\t\t}\n\t\tfor rows.Next() {\n\t\t\tvar issueID, dependsOnID, depType string\n\t\t\tif err := rows.Scan(&issueID, &dependsOnID, &depType); err != nil {\n\t\t\t\t_ = rows.Close()\n\t\t\t\treturn fmt.Errorf(\"dependency graph: scan %s: %w\", depTable, err)\n\t\t\t}\n\t\t\tt := types.DependencyType(depType)\n\t\t\tif t == types.DepBlocks || t == types.DepConditionalBlocks || (includeParentChild && t == types.DepParentChild) {\n\t\t\t\tgraph[issueID] = append(graph[issueID], dependsOnID)\n\t\t\t}\n\t\t}\n\t\t_ = rows.Close()\n\t\tif err := rows.Err(); err != nil {\n\t\t\treturn fmt.Errorf(\"dependency graph: rows %s: %w\", depTable, err)\n\t\t}\n\t}\n\treturn nil\n}\n\n// CycleThroughEdgesInGraph reports a rendered cycle that traverses\n// one of the new edges (issueID -> dependsOnID pairs), or \"\" when no new edge\n// lies on a cycle. An edge u -> v is on a cycle exactly when u is reachable\n// from v, so this is precise where cycle enumeration is not: a DFS-based\n// detector records one cycle per back edge and can report a pre-existing\n// cycle through the same nodes instead of the one the new edge created\n// (bd-578h9.9). The graph must already contain the new edges.\nfunc CycleThroughEdgesInGraph(graph map[string][]string, edges [][2]string) string {\n\tfor _, edge := range edges {\n\t\tsource, target := edge[0], edge[1]\n\t\tif source == \"\" || target == \"\" {\n\t\t\tcontinue\n\t\t}","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/cycles.go#L99-L135","documentation":"After iterating a dependency table's rows, appendDependencyGraphInTx checks rows.Err() and wraps any deferred iteration error as 'dependency graph: rows <table>: %w'. This catches driver-level failures that surface mid-iteration (connection drop, query abort) rather than at query or scan time.","triggerScenarios":"The rows iterator encounters a transport/backend error while streaming rows of a dependency table during graph assembly — network drop to the DB, server abort, timeout.","commonSituations":"Large graphs over flaky/remote Dolt connections; query timeouts on very wide dependency tables; server restart mid-scan.","solutions":["Inspect the wrapped error for transport/timeout causes and retry the operation","Reduce graph size or batch the check to shorten row streaming","Ensure a stable DB connection (timeouts, keepalives) for remote backends"],"exampleFix":"// before\n// treat nil error after loop as success only\n// after\nif err := rows.Err(); err != nil { return fmt.Errorf(\"...: %w\", err) } // already present; retry op on failure","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil && strings.HasPrefix(err.Error(), \"dependency graph: rows \") {\n    cause := errors.Unwrap(err)\n    // transient network/backend error: backoff and retry the whole operation\n    return err\n}","preventionTips":["Use stable, low-latency connections to the DB for graph checks","Set generous query timeouts for large dependency tables","Chunk cycle detection for very large graphs"],"tags":["database","rows-iteration","dependency-graph","network"],"backgroundTag":"dependency-graph-rows-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}