{"record":{"id":"680e148b7001e6c4","repo":"gastownhall/beads","slug":"db-labelsqlrepository-listbyissueids-scan-w","errorCode":null,"errorMessage":"db: LabelSQLRepository.ListByIssueIDs: scan: %w","messagePattern":"db: LabelSQLRepository\\.ListByIssueIDs: scan: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/db/label.go","lineNumber":179,"sourceCode":"\t\tplaceholders[i] = \"?\"\n\t\targs[i] = id\n\t}\n\ttable := pickLabelTable(opts.UseWispsTable)\n\t//nolint:gosec // G201: table is one of two hardcoded constants\n\tq := fmt.Sprintf(\n\t\t\"SELECT issue_id, label FROM %s WHERE issue_id IN (%s) ORDER BY issue_id, label\",\n\t\ttable, strings.Join(placeholders, \",\"),\n\t)\n\trows, err := r.runner.QueryContext(ctx, q, args...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"db: LabelSQLRepository.ListByIssueIDs: %w\", err)\n\t}\n\tdefer rows.Close()\n\n\tfor rows.Next() {\n\t\tvar issueID, label string\n\t\tif err := rows.Scan(&issueID, &label); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"db: LabelSQLRepository.ListByIssueIDs: scan: %w\", err)\n\t\t}\n\t\tresult[issueID] = append(result[issueID], label)\n\t}\n\tif err := rows.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"db: LabelSQLRepository.ListByIssueIDs: rows: %w\", err)\n\t}\n\treturn result, nil\n}\n\nfunc (r *labelSQLRepositoryImpl) DeleteAllForIDs(ctx context.Context, ids []string, opts domain.LabelOpts) (int, error) {\n\tif len(ids) == 0 {\n\t\treturn 0, nil\n\t}\n\ttable := \"labels\"\n\tif opts.UseWispsTable {\n\t\ttable = \"wisp_labels\"\n\t}\n\ttotal := 0","sourceCodeStart":161,"sourceCodeEnd":197,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/label.go#L161-L197","documentation":"LabelSQLRepository.ListByIssueIDs failed while scanning one (issue_id, label) row into two strings. The query ran but a returned row could not be decoded into the destination types.","triggerScenarios":"Calling ListByIssueIDs when a row has NULL issue_id or NULL label, or the driver yields a column type incompatible with string.","commonSituations":"Schema drift making issue_id/label nullable; a corrupted or manually edited labels table; custom driver returning non-standard types (e.g. binary issue_id columns).","solutions":["Unwrap the error to identify which column failed to scan","Check the table schema: issue_id and label must be NOT NULL string/text columns","Migrate or delete NULL/malformed rows in the labels table","If values can be NULL legitimately, scan into sql.NullString and convert after"],"exampleFix":"// before\nvar issueID, label string\nif err := rows.Scan(&issueID, &label); err != nil { ... }\n// after\nvar issueID, label sql.NullString\nif err := rows.Scan(&issueID, &label); err != nil { ... }\nif issueID.Valid && label.Valid { result[issueID.String] = append(...) }","handlingStrategy":"try-catch","validationCode":"// check schema nullability before reads\n// SELECT is_nullable FROM information_schema.columns WHERE table_name='issue_labels' AND column_name IN ('issue_id','label')","typeGuard":"func isScanErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"scan: \")\n}","tryCatchPattern":"m, err := repo.ListByIssueIDs(ctx, ids, opts)\nif err != nil {\n    if strings.Contains(err.Error(), \"scan: \") {\n        log.Printf(\"bad row in labels: %v\", errors.Unwrap(err))\n    }\n    return err\n}","preventionTips":["Keep issue_id and label NOT NULL","Avoid manual edits to the labels table","Scan NullString variants if schema allows NULLs","Add a migration-consistency check at startup"],"tags":["go","database","sql","scan"],"backgroundTag":"sql-row-scan-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}