gastownhall/beads · error

db: LabelSQLRepository.List: rows: %w

Error message

db: LabelSQLRepository.List: rows: %w

What it means

LabelSQLRepository.List failed at the end of iteration: rows.Err() returned non-nil after all rows were consumed. This indicates the rows stream broke mid-iteration (connection loss, driver error, context cancellation) rather than a per-row conversion failure.

Source

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

	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] = "?"
		args[i] = id
	}
	table := pickLabelTable(opts.UseWispsTable)
	//nolint:gosec // G201: table is one of two hardcoded constants
	q := fmt.Sprintf(

View on GitHub (pinned to 71377f2769)

Solutions

  1. Unwrap the error to see the driver-level cause (context deadline, connection reset, etc.)
  2. Check caller-side context timeouts: increase the deadline or pass a non-cancelled context
  3. Verify database connectivity/restart if the connection was dropped
  4. If datasets are large, paginate or narrow the query instead of streaming all rows in one call

Example fix

// before
ctx := context.Background()
labels, _ := repo.List(ctx, opts)
// after
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
labels, _ := repo.List(ctx, opts)
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure context has sane deadline before call
if _, ok := ctx.Deadline(); !ok {
    var cancel context.CancelFunc
    ctx, cancel = context.WithTimeout(ctx, 30*time.Second)
    defer cancel()
}

Try / catch

labels, err := repo.List(ctx, opts)
if err != nil {
    var cause error
    errors.As(err, &cause)
    if errors.Is(err, context.DeadlineExceeded) || isNetErr(cause) {
        // retry with backoff
    }
    return err
}

Prevention

When it happens

Trigger: Calling List when the underlying connection drops or the context is cancelled/times out while rows are still being iterated.

Common situations: Long-running queries over large label sets hitting context deadlines; network interruption to a remote database; embedded database closed mid-query; driver-level errors surfaced only after iteration completes.

Related errors


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