{"record":{"id":"898f65c401e9f366","repo":"gastownhall/beads","slug":"dependency-graph-scan-s-w","errorCode":null,"errorMessage":"dependency graph: scan %s: %w","messagePattern":"dependency graph: scan (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/cycles.go","lineNumber":108,"sourceCode":"// graph. DetectCycles intentionally continues to use AppendBlockingGraphInTx.\nfunc AppendSchedulingGraphInTx(ctx context.Context, tx DBTX, depTables []string, graph map[string][]string) error {\n\treturn appendDependencyGraphInTx(ctx, tx, depTables, graph, true)\n}\n\nfunc appendDependencyGraphInTx(ctx context.Context, tx DBTX, depTables []string, graph map[string][]string, includeParentChild bool) error {\n\tfor _, depTable := range depTables {\n\t\trows, err := tx.QueryContext(ctx, fmt.Sprintf(`\n\t\t\tSELECT issue_id, %s AS depends_on_id, type\n\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","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/cycles.go#L90-L126","documentation":"appendDependencyGraphInTx failed scanning a row of a dependency table into (issue_id, depends_on_id, type). A scan error means column types or nullability differ from what the query expects; the function closes the rows and wraps the error with the table name for diagnosis.","triggerScenarios":"A dependency row has NULL in issue_id/depends_on_id/type or an unexpected column type, so rows.Scan fails while iterating during graph assembly.","commonSituations":"Schema mismatch after version upgrade (columns added/renamed/nullable); hand-edited or imported rows with NULL dependency fields; corruption from a failed migration.","solutions":["Find and fix the offending NULL/malformed rows in the named dependency table","Align the schema with the version of beads in use (re-run migrations)","If an upgrade caused it, check release notes for dependency-table schema changes"],"exampleFix":"// before\nSELECT issue_id, depends_on_id, type FROM dependencies -- NULL rows present\n// after\nUPDATE dependencies SET type='blocks' WHERE type IS NULL; -- then retry","handlingStrategy":"validation","validationCode":"// detect malformed rows before creating dependencies\nrows, _ := db.Query(\"SELECT issue_id, depends_on_id, type FROM dependencies WHERE issue_id IS NULL OR depends_on_id IS NULL OR type IS NULL\")\n// if any rows returned, repair them first","typeGuard":null,"tryCatchPattern":"if err != nil && strings.HasPrefix(err.Error(), \"dependency graph: scan \") {\n    // locate NULL rows in the named table, repair, then retry\n}","preventionTips":["Enforce NOT NULL on dependency columns","Repair data after version upgrades","Validate imported dependency rows before writing them"],"tags":["database","scan","schema","dependency-graph"],"backgroundTag":"dependency-graph-scan-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}