{"record":{"id":"e3c34903ec78e7c0","repo":"gastownhall/beads","slug":"scan-dependent-w","errorCode":null,"errorMessage":"scan dependent: %w","messagePattern":"scan dependent: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/delete.go","lineNumber":442,"sourceCode":"\t\ttoProcess = toProcess[batchEnd:]\n\n\t\tinClause, args := buildSQLInClause(batch)\n\t\tfor _, depTable := range []string{\"dependencies\", \"wisp_dependencies\"} {\n\t\t\trows, err := tx.QueryContext(ctx,\n\t\t\t\tfmt.Sprintf(`SELECT issue_id FROM %s WHERE %s`, depTable, depTargetIn(\"\", inClause)),\n\t\t\t\targs...)\n\t\t\tif err != nil {\n\t\t\t\tif optionalBlockedTable(depTable) && isTableNotExistError(err) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn nil, fmt.Errorf(\"query dependents for batch from %s: %w\", depTable, err)\n\t\t\t}\n\n\t\t\tfor rows.Next() {\n\t\t\t\tvar depID string\n\t\t\t\tif err := rows.Scan(&depID); err != nil {\n\t\t\t\t\t_ = rows.Close()\n\t\t\t\t\treturn nil, fmt.Errorf(\"scan dependent: %w\", err)\n\t\t\t\t}\n\t\t\t\tif !result[depID] {\n\t\t\t\t\tresult[depID] = true\n\t\t\t\t\ttoProcess = append(toProcess, depID)\n\t\t\t\t}\n\t\t\t}\n\t\t\t_ = rows.Close()\n\t\t\tif err := rows.Err(); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"iterate dependents for batch from %s: %w\", depTable, err)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn result, nil\n}\n\n//nolint:gosec // G201: table is selected by callers from fixed issue/wisp auxiliary tables.\nfunc CountRowsForIssueIDsInTx(ctx context.Context, tx DBTX, table string, ids []string) (int, error) {","sourceCodeStart":424,"sourceCodeEnd":460,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/delete.go#L424-L460","documentation":"While iterating dependents rows in FindAllDependentsInTx, rows.Scan into a single string column (issue_id) failed. This means the result row could not be decoded into a string — usually a driver-level decode error or an unexpected column shape, not a data problem.","triggerScenarios":"Cascade traversal (ResolveDeletionSetInTx → FindAllDependentsInTx) where the SELECT returns a row whose value cannot scan into a string — e.g. NULL issue_id in a corrupted dependency row, or a driver type mismatch.","commonSituations":"Corrupted dependency rows after a failed import/sync inserting NULL or non-string issue_id values; driver version mismatch producing unexpected column types.","solutions":["Find and repair the offending row: SELECT * FROM dependencies WHERE issue_id IS NULL and fix or delete it","Re-run bd doctor / integrity checks to detect corrupt dependency rows","Re-import or re-sync the dependency data if corruption came from a bad sync","Update the storage driver if a type-mismatch bug is implicated"],"exampleFix":"-- before (corrupt row)\nINSERT INTO dependencies (issue_id, depends_on_id) VALUES (NULL, 'bd-1');\n-- after\nINSERT INTO dependencies (issue_id, depends_on_id) VALUES ('bd-2', 'bd-1');","handlingStrategy":"validation","validationCode":"// detect corrupt dependency rows before deleting\nrows, _ := db.Query(\"SELECT issue_id FROM dependencies WHERE issue_id IS NULL\")\n// any result => repair rows before cascade delete","typeGuard":null,"tryCatchPattern":"_, err := DeleteIssuesInTx(ctx, tx, ids, WithCascade())\nif err != nil && strings.Contains(err.Error(), \"scan dependent:\") {\n\trepairNullDependencyRows(db) // fix or remove bad rows, then retry\n\treturn retry(ctx, ids)\n}\nreturn err","preventionTips":["Validate imported/synced dependency rows (no NULL issue_id)","Run integrity checks after imports and syncs","Keep the storage driver up to date"],"tags":["database","scan","corruption","dependency-graph"],"backgroundTag":"row-scan-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}