gastownhall/beads · warning

db: LabelSQLRepository.List: issueID must not be empty

Error message

db: LabelSQLRepository.List: issueID must not be empty

What it means

Input validation error thrown by LabelSQLRepository.List when issueID is empty. Listing labels for an empty ID is meaningless and would either error in SQL or return unrelated rows, so the repository rejects the call immediately with a nil result.

Source

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

		return fmt.Errorf("db: LabelSQLRepository.Delete %s/%s: rows affected: %w", issueID, label, err)
	}
	if rows == 0 {
		return nil
	}
	if err := r.events.Record(ctx, domain.Event{
		IssueID:  issueID,
		Type:     types.EventLabelRemoved,
		Actor:    actor,
		OldValue: label,
	}, domain.RecordEventOpts{UseWispsTable: opts.UseWispsTable}); err != nil {
		return err
	}
	return issueops.RecordEventInTx(ctx, r.runner, issueops.EventUpdate, issueID, actor)
}

func (r *labelSQLRepositoryImpl) List(ctx context.Context, issueID string, opts domain.LabelOpts) ([]string, error) {
	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)
		}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Populate issueID before calling List
  2. Return early on upstream errors instead of continuing with empty ID
  3. Validate the issue record structure before repository calls

Example fix

// before
labels, _ := issueRepo.Get(ctx, id)
out, _ := labelRepo.List(ctx, issue.ID, opts) // issue may be zero-value
// after
if err != nil { return err }
if issue.ID == "" { return fmt.Errorf("issue not loaded") }
out, err := labelRepo.List(ctx, issue.ID, opts)
Defensive patterns

Strategy: validation

Validate before calling

if issueID == "" { return fmt.Errorf("cannot list labels: issueID required") }

Type guard

func hasIssueID(i Issue) bool { return i.ID != "" }

Try / catch

labels, err := repo.List(ctx, issueID, opts)
if err != nil {
    if strings.Contains(err.Error(), "issueID must not be empty") { return nil, fmt.Errorf("issue not loaded before List") }
    return nil, err
}

Prevention

When it happens

Trigger: Calling List(ctx, "", opts) — from a zero-value issue struct, a lookup that failed upstream and returned an empty ID, or argument misordering.

Common situations: Code path that ignores an earlier error and proceeds with an empty issueID; JSON payloads missing the id field; batch jobs iterating records where one record has no ID.

Related errors


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