gastownhall/beads · warning
db: LabelSQLRepository.Delete: issueID must not be empty
Error message
db: LabelSQLRepository.Delete: issueID must not be empty
What it means
Input validation error thrown by LabelSQLRepository.Delete when the issueID argument is an empty string. The library refuses to issue a DELETE with an unconstrained or meaningless key, so the caller gets an immediate error instead of a silent no-op or accidental mass deletion.
Source
Thrown at internal/storage/domain/db/label.go:92
}
return nil
}
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 {View on GitHub (pinned to 71377f2769)
Solutions
- Ensure issueID is populated before calling Delete
- Add caller-side validation of issueID
- Check upstream parsing/deserialization that produced the empty ID
Example fix
// before
repo.Delete(ctx, issueID, label, actor, opts) // issueID may be ""
// after
if issueID == "" { return fmt.Errorf("issueID required") }
return repo.Delete(ctx, issueID, label, actor, opts) Defensive patterns
Strategy: validation
Validate before calling
if err := validateIssueID(issueID); err != nil { return err }
// where
func validateIssueID(id string) error {
if id == "" { return fmt.Errorf("issueID required") }
return nil
} Type guard
func hasIssueID(i Issue) bool { return i.ID != "" } Try / catch
if err := repo.Delete(ctx, issueID, label, actor, opts); err != nil {
if strings.Contains(err.Error(), "issueID must not be empty") { return fmt.Errorf("bug: empty issueID passed to Delete") }
return err
} Prevention
- Validate IDs at the CLI/API boundary before repository calls
- Never ignore upstream errors that produce empty IDs
- Use constructors that enforce non-empty IDs
- Add unit tests covering empty-argument paths
When it happens
Trigger: Calling Delete(ctx, "", label, actor, opts) — typically from an uninitialized variable, a zero-value struct field, or a caller that skipped its own input checks.
Common situations: Deserialization failure leaving IssueID empty; calling Delete before assigning fields; wiring bug passing wrong argument position into Delete.
Related errors
- db: LabelSQLRepository.Delete: label must not be empty
- db: LabelSQLRepository.List: issueID must not be empty
- %s cannot be empty
- no rules to compact
- memory content cannot be empty
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/3787da354019796a.
Report an issue: GitHub.