gastownhall/beads · error

db: LabelSQLRepository.List: scan: %w

Error message

db: LabelSQLRepository.List: scan: %w

What it means

LabelSQLRepository.List failed while scanning a row from the labels table into a string. database/sql's rows.Scan returned an error for the current row, and the repository wraps it with this context. It means the query succeeded but a row could not be converted into the destination type.

Source

Thrown at internal/storage/domain/db/label.go:143

	if issueID == "" {
		return nil, fmt.Errorf("db: LabelSQLRepository.List: issueID must not be empty")
	}
	table := pickLabelTable(opts.UseWispsTable)
	//nolint:gosec // G201: table is one of two hardcoded constants
	rows, err := r.runner.QueryContext(ctx,
		fmt.Sprintf("SELECT label FROM %s WHERE issue_id = ? ORDER BY label", table),
		issueID,
	)
	if err != nil {
		return nil, fmt.Errorf("db: LabelSQLRepository.List %s: %w", issueID, err)
	}
	defer rows.Close()

	var out []string
	for rows.Next() {
		var label string
		if err := rows.Scan(&label); err != nil {
			return nil, fmt.Errorf("db: LabelSQLRepository.List: scan: %w", err)
		}
		out = append(out, label)
	}
	if err := rows.Err(); err != nil {
		return nil, fmt.Errorf("db: LabelSQLRepository.List: rows: %w", err)
	}
	return out, nil
}

func (r *labelSQLRepositoryImpl) ListByIssueIDs(ctx context.Context, issueIDs []string, opts domain.LabelOpts) (map[string][]string, error) {
	result := make(map[string][]string)
	if len(issueIDs) == 0 {
		return result, nil
	}
	placeholders := make([]string, len(issueIDs))
	args := make([]any, len(issueIDs))
	for i, id := range issueIDs {
		placeholders[i] = "?"

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error via errors.Unwrap/As to see the exact scan failure (unsupported Scan, converting NULL to string, etc.)
  2. Check the labels table schema: the label column should be NOT NULL and text-typed; migrate any NULL rows
  3. If NULLs are expected, alter the query to use COALESCE(label, '') or scan into a *string/sql.NullString
  4. Verify the driver in use matches the schema (e.g. re-run schema migrations for the embedded Dolt database)

Example fix

// before
var label string
if err := rows.Scan(&label); err != nil { ... }
// after
var label sql.NullString
if err := rows.Scan(&label); err != nil { ... }
if label.Valid { out = append(out, label.String) }
Defensive patterns

Strategy: try-catch

Validate before calling

// before calling List, verify schema
var notNull, dtype string
err := rawRepo.QueryRow(ctx, `SELECT COUNT(*) FROM information_schema.columns WHERE table_name='issue_labels' AND column_name='label' AND is_nullable='NO'`).Scan(&notNull)
// or check migrations ran before opening repos

Type guard

func isScanErr(err error) bool {
    var s *fmt.ScanError // or check strings.Contains(err.Error(), "Scan")
    return err != nil && strings.Contains(err.Error(), "unsupported Scan")
}

Try / catch

labels, err := repo.List(ctx, opts)
if err != nil {
    if strings.Contains(err.Error(), "scan: ") {
        // inspect cause
        log.Printf("label scan failed: %v", errors.Unwrap(err))
    }
    return err
}

Prevention

When it happens

Trigger: Calling List when a row value is not scannable into *string — e.g. a NULL in the label column, or a driver returning an unexpected type for the label column.

Common situations: Schema drift after a migration changed the label column to nullable or to a non-string type; a custom or mocked driver returning exotic types (e.g. []byte with non-UTF8 data handled unexpectedly); using an embedded/Dolt driver with different NULL handling.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/ef7b9f9f5ae99fdf. Report an issue: GitHub.