gastownhall/beads · error
db: LabelSQLRepository.ListByIssueIDs: scan: %w
Error message
db: LabelSQLRepository.ListByIssueIDs: scan: %w
What it means
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.
Source
Thrown at internal/storage/domain/db/label.go:179
placeholders[i] = "?"
args[i] = id
}
table := pickLabelTable(opts.UseWispsTable)
//nolint:gosec // G201: table is one of two hardcoded constants
q := fmt.Sprintf(
"SELECT issue_id, label FROM %s WHERE issue_id IN (%s) ORDER BY issue_id, label",
table, strings.Join(placeholders, ","),
)
rows, err := r.runner.QueryContext(ctx, q, args...)
if err != nil {
return nil, fmt.Errorf("db: LabelSQLRepository.ListByIssueIDs: %w", err)
}
defer rows.Close()
for rows.Next() {
var issueID, label string
if err := rows.Scan(&issueID, &label); err != nil {
return nil, fmt.Errorf("db: LabelSQLRepository.ListByIssueIDs: scan: %w", err)
}
result[issueID] = append(result[issueID], label)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("db: LabelSQLRepository.ListByIssueIDs: rows: %w", err)
}
return result, nil
}
func (r *labelSQLRepositoryImpl) DeleteAllForIDs(ctx context.Context, ids []string, opts domain.LabelOpts) (int, error) {
if len(ids) == 0 {
return 0, nil
}
table := "labels"
if opts.UseWispsTable {
table = "wisp_labels"
}
total := 0View on GitHub (pinned to 71377f2769)
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
Example fix
// before
var issueID, label string
if err := rows.Scan(&issueID, &label); err != nil { ... }
// after
var issueID, label sql.NullString
if err := rows.Scan(&issueID, &label); err != nil { ... }
if issueID.Valid && label.Valid { result[issueID.String] = append(...) } Defensive patterns
Strategy: try-catch
Validate before calling
// check schema nullability before reads
// SELECT is_nullable FROM information_schema.columns WHERE table_name='issue_labels' AND column_name IN ('issue_id','label') Type guard
func isScanErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "scan: ")
} Try / catch
m, err := repo.ListByIssueIDs(ctx, ids, opts)
if err != nil {
if strings.Contains(err.Error(), "scan: ") {
log.Printf("bad row in labels: %v", errors.Unwrap(err))
}
return err
} Prevention
- 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
When it happens
Trigger: Calling ListByIssueIDs when a row has NULL issue_id or NULL label, or the driver yields a column type incompatible with string.
Common situations: 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).
Related errors
- db: ChildCounterSQLRepository.NextChildID: scan: %w
- db: CommentSQLRepository.CountsByIssueIDs: scan: %w
- db: CommentSQLRepository.ListByIssueIDs: scan: %w
- db: LabelSQLRepository.List: scan: %w
- get issues by IDs: scan: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/680e148b7001e6c4.
Report an issue: GitHub.