gastownhall/beads · warning

db: LabelSQLRepository.Delete: label must not be empty

Error message

db: LabelSQLRepository.Delete: label must not be empty

What it means

Input validation error thrown by LabelSQLRepository.Delete when the label argument is an empty string. An empty label would not match any row meaningfully, so the repository rejects the call up front rather than executing a pointless DELETE and journaling a spurious event.

Source

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

	if err := r.events.Record(ctx, domain.Event{
		IssueID:  issueID,
		Type:     types.EventLabelAdded,
		Actor:    actor,
		NewValue: label,
	}, domain.RecordEventOpts{UseWispsTable: opts.UseWispsTable}); err != nil {
		return err
	}
	// A label is part of the bead snapshot; the idempotent no-op path above
	// returns without writing and journals nothing.
	return issueops.RecordEventInTx(ctx, r.runner, issueops.EventUpdate, issueID, actor)
}

func (r *labelSQLRepositoryImpl) Delete(ctx context.Context, issueID, label, actor string, opts domain.LabelOpts) error {
	if issueID == "" {
		return fmt.Errorf("db: LabelSQLRepository.Delete: issueID must not be empty")
	}
	if label == "" {
		return fmt.Errorf("db: LabelSQLRepository.Delete: label must not be empty")
	}
	table := pickLabelTable(opts.UseWispsTable)
	//nolint:gosec // G201: table is one of two hardcoded constants
	result, err := r.runner.ExecContext(ctx,
		fmt.Sprintf("DELETE FROM %s WHERE issue_id = ? AND label = ?", table),
		issueID, label,
	)
	if err != nil {
		return fmt.Errorf("db: LabelSQLRepository.Delete %s/%s: %w", issueID, label, err)
	}
	rows, err := result.RowsAffected()
	if err != nil {
		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{

View on GitHub (pinned to 71377f2769)

Solutions

  1. Ensure label is non-empty before calling Delete
  2. Validate/sanitize label input at the CLI/UI boundary
  3. Check string-splitting logic that produced an empty label

Example fix

// before
label := strings.TrimSpace(userLabel)
repo.Delete(ctx, issueID, label, actor, opts)
// after
label := strings.TrimSpace(userLabel)
if label == "" { return fmt.Errorf("label required") }
repo.Delete(ctx, issueID, label, actor, opts)
Defensive patterns

Strategy: validation

Validate before calling

label = strings.TrimSpace(rawLabel)
if label == "" { return fmt.Errorf("label must not be empty") }

Try / catch

if err := repo.Delete(ctx, issueID, label, actor, opts); err != nil {
    if strings.Contains(err.Error(), "label must not be empty") { return fmt.Errorf("cannot remove empty label for %s", issueID) }
    return err
}

Prevention

When it happens

Trigger: Calling Delete(ctx, issueID, "", actor, opts) — e.g. a label variable lost in string processing, or user input stripped to empty by sanitization.

Common situations: Trimmed user input that was only whitespace; label field never set on a struct before persistence; parsing bug splitting labels on the wrong delimiter.

Related errors


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