{"record":{"id":"7b20abd4e129d4cb","repo":"gastownhall/beads","slug":"scan-issue-id-w","errorCode":null,"errorMessage":"scan issue ID: %w","messagePattern":"scan issue ID: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/bulk_ops.go","lineNumber":185,"sourceCode":"\t\t_ = rows.Close()\n\t}\n\treturn nil\n}\n\n// DeleteIssuesBySourceRepoInTx removes all issues from a source repo and their related data.\n//\n//nolint:gosec // G201: table is validated by hardcoded list\nfunc DeleteIssuesBySourceRepoInTx(ctx context.Context, tx *sql.Tx, sourceRepo string) (int, error) {\n\trows, err := tx.QueryContext(ctx, `SELECT id FROM issues WHERE source_repo = ?`, sourceRepo)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"query issues: %w\", err)\n\t}\n\tvar issueIDs []string\n\tfor rows.Next() {\n\t\tvar id string\n\t\tif err := rows.Scan(&id); err != nil {\n\t\t\t_ = rows.Close()\n\t\t\treturn 0, fmt.Errorf(\"scan issue ID: %w\", err)\n\t\t}\n\t\tissueIDs = append(issueIDs, id)\n\t}\n\t_ = rows.Close()\n\n\tif len(issueIDs) == 0 {\n\t\treturn 0, nil\n\t}\n\n\taffectedIssues, affectedWisps, aerr := AffectedByDeletionInTx(ctx, tx, issueIDs, nil)\n\tif aerr != nil {\n\t\treturn 0, fmt.Errorf(\"affected by source-repo delete: %w\", aerr)\n\t}\n\n\t// Deleted issues hold no leases: clear them while the id set is still\n\t// joinable (before the issues rows go away).\n\tif _, err := tx.ExecContext(ctx,\n\t\t`DELETE FROM leases WHERE issue_id IN (SELECT id FROM issues WHERE source_repo = ?)`, sourceRepo); err != nil {","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/bulk_ops.go#L167-L203","documentation":"While iterating the source_repo id results, rows.Scan(&id) failing produces \"scan issue ID: %w\". Since only a single text id column is selected, this almost always means the id column is NULL or returned in a type the driver cannot convert to string.","triggerScenarios":"rows.Scan errors on the single id column: NULL id in the issues table (corrupted/manually modified DB), or driver type conversion failure for the id value.","commonSituations":"Corrupted database from a crashed legacy migration; manually edited rows with NULL primary keys; a non-SQLite driver returning ids as non-string types.","solutions":["Inspect the wrapped error — likely \"converting NULL to string is unsupported\" — and find/fix the NULL id row","Restore from backup or re-run integrity checks (e.g. PRAGMA integrity_check) if the DB is corrupted","Run pending migrations to rebuild any malformed rows","If a custom driver is in use, ensure it returns ids in a string-convertible type"],"exampleFix":"null","handlingStrategy":"type-guard","validationCode":"// detect corrupt rows before the bulk delete\nrows, _ := db.Query(\"SELECT COUNT(*) FROM issues WHERE id IS NULL OR id = ''\")","typeGuard":"var convErr *sql.ConvertError\nif errors.As(err, &convErr) { /* convErr identifies the bad value/column */ }","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"converting NULL\") {\n    // stop and repair the database; do not proceed with a partial delete\n    return fmt.Errorf(\"corrupt issue id row: %w\", err)\n}","preventionTips":["Run integrity checks after crashes or forced shutdowns","Restore from backup rather than patching corrupt rows","Keep primary keys NOT NULL and let the schema enforce them"],"tags":["database","sql-scan","data-corruption"],"backgroundTag":"sql-scan-error","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}