{"record":{"id":"881320eee11ff04f","repo":"gastownhall/beads","slug":"get-dependencies-scan-w","errorCode":null,"errorMessage":"get dependencies: scan: %w","messagePattern":"get dependencies: scan: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependencies.go","lineNumber":1116,"sourceCode":"//nolint:gosec // G201: table names come from hardcoded constants\nfunc GetDependenciesWithMetadataInTx(ctx context.Context, tx DBTX, issueID string) ([]*types.IssueWithDependencyMetadata, error) {\n\ttype depMeta struct {\n\t\tdepID, depType string\n\t}\n\n\t// Query both dependency tables to find all dependencies.\n\tvar deps []depMeta\n\tfor _, depTable := range []string{\"dependencies\", \"wisp_dependencies\"} {\n\t\trows, err := tx.QueryContext(ctx, fmt.Sprintf(\n\t\t\t`SELECT %s AS depends_on_id, type FROM %s WHERE issue_id = ?`, DepTargetExpr, depTable), issueID)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"get dependencies from %s: %w\", depTable, err)\n\t\t}\n\t\tfor rows.Next() {\n\t\t\tvar d depMeta\n\t\t\tif scanErr := rows.Scan(&d.depID, &d.depType); scanErr != nil {\n\t\t\t\t_ = rows.Close()\n\t\t\t\treturn nil, fmt.Errorf(\"get dependencies: scan: %w\", scanErr)\n\t\t\t}\n\t\t\tdeps = append(deps, d)\n\t\t}\n\t\t_ = rows.Close()\n\t\tif err := rows.Err(); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"get dependencies: rows from %s: %w\", depTable, err)\n\t\t}\n\t}\n\n\tif len(deps) == 0 {\n\t\treturn nil, nil\n\t}\n\n\t// Fetch all dependency target issues.\n\tids := make([]string, len(deps))\n\tfor i, d := range deps {\n\t\tids[i] = d.depID\n\t}","sourceCodeStart":1098,"sourceCodeEnd":1134,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependencies.go#L1098-L1134","documentation":"Wraps a rows.Scan failure while decoding a (depends_on_id, type) pair inside GetDependenciesWithMetadataInTx. The dependency query succeeded but a row's values cannot be scanned into the two string fields.","triggerScenarios":"rows.Scan(&d.depID, &d.depType) fails — the type column or the DepTargetExpr-derived depends_on_id is NULL or of an unexpected type in a dependencies/wisp_dependencies row.","commonSituations":"Rows inserted with NULL dep_type by older versions or manual edits; DepTargetExpr returning unexpected shapes (e.g. NULL for dangling references after a partial delete).","solutions":["Find and fix the offending dependency row (NULL type or dangling depends_on reference)","Clean up orphaned dependency rows referencing deleted issues","Align schema version with the binary (run migrations) so column types match the scanner"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Data preflight: find dependency rows that will fail the (string,string) scan:\nrows, err := db.QueryContext(ctx, `SELECT issue_id FROM dependencies WHERE type IS NULL`)\nrows2, err2 := db.QueryContext(ctx, `SELECT issue_id FROM wisp_dependencies WHERE type IS NULL`)\n// Any results indicate corrupt rows to repair before fetching dependencies.","typeGuard":null,"tryCatchPattern":"deps, err := ops.GetDependenciesWithMetadataInTx(ctx, tx, issueID)\nif err != nil && strings.Contains(err.Error(), \"get dependencies: scan\") {\n    // Deterministic — repair data instead of retrying:\n    return fmt.Errorf(\"corrupt dependency row (NULL type/target); run repair: %w\", err)\n}","preventionTips":["Enforce NOT NULL on dep_type/depends_on columns in migrations","Use library APIs (not manual SQL) to insert dependencies","Clean orphaned dependency rows when issues are deleted externally","Run integrity checks after version upgrades or crashes"],"tags":["database","sql","scan","dependencies","go"],"backgroundTag":"row-scan-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}