gastownhall/beads · error
get labels for issues: scan: %w
Error message
get labels for issues: scan: %w
What it means
This error wraps a rows.Scan failure in getLabelsIntoFromTable while decoding an (issue_id, label) row pair into strings. It indicates a row whose columns are NULL or of unexpected types in the batched label fetch. The library closes rows and aborts the whole batch rather than returning partial label maps.
Source
Thrown at internal/storage/issueops/labels.go:119
}
batch := ids[start:end]
placeholders := make([]string, len(batch))
args := make([]any, len(batch))
for i, id := range batch {
placeholders[i] = "?"
args[i] = id
}
rows, err := tx.QueryContext(ctx, fmt.Sprintf(
`SELECT issue_id, label FROM %s WHERE issue_id IN (%s) ORDER BY issue_id, label`,
labelTable, strings.Join(placeholders, ",")), args...)
if err != nil {
return fmt.Errorf("get labels for issues from %s: %w", labelTable, err)
}
for rows.Next() {
var issueID, label string
if err := rows.Scan(&issueID, &label); err != nil {
_ = rows.Close()
return fmt.Errorf("get labels for issues: scan: %w", err)
}
result[issueID] = append(result[issueID], label)
}
_ = rows.Close()
if err := rows.Err(); err != nil {
return fmt.Errorf("get labels for issues: rows: %w", err)
}
}
return nil
}
// AddLabelInTx adds a label to an issue and records an event within an existing
// transaction. Automatically routes to wisp tables if the ID is an active wisp.
// Uses INSERT IGNORE for idempotency.
func AddLabelInTx(ctx context.Context, tx DBTX, labelTable, eventTable, issueID, label, actor string) error {
// Reject an over-length label up front. The INSERT IGNORE below would
// otherwise silently truncate it to the VARCHAR(255) column, storing a label
// the caller never sent; a typed ErrFieldTooLong is the clean rejection.View on GitHub (pinned to 71377f2769)
Solutions
- Delete or repair rows with NULL issue_id/label: DELETE FROM labels WHERE issue_id IS NULL OR label IS NULL
- Add NOT NULL constraints to prevent recurrence
- If soft handling is needed, patch the scan to use sql.NullString and skip invalid rows
Defensive patterns
Strategy: validation
Validate before calling
var nulls int
_ = tx.QueryRow(`SELECT COUNT(*) FROM labels WHERE issue_id IS NULL OR label IS NULL`).Scan(&nulls)
if nulls > 0 { return fmt.Errorf("%d malformed label rows; repair before bulk hydration", nulls) } Try / catch
m, err := issueops.GetLabelsForIssuesInTx(ctx, tx, ids)
if err != nil {
if strings.Contains(err.Error(), "scan") {
// locate and repair NULL rows, then retry
}
return err
} Prevention
- Add NOT NULL constraints to issue_id and label columns
- Sanitize data imported from external tools
- Avoid manual SQL inserts into storage tables
- Schedule integrity checks for NULL values in aux tables
When it happens
Trigger: Rows in labels/wisp_labels with NULL issue_id or label values encountered during GetLabelsForIssuesInTx batch iteration.
Common situations: Corrupted or manually inserted rows; schema drift allowing NULLs; partial imports from migrations.
Related errors
- get labels: scan: %w
- db: GetAllConfig: scan: %w
- db: GetCustomTypes: scan custom_types: %w
- ErrScan
- failed to scan dependency keys: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/39049630470b54c2.
Report an issue: GitHub.