gastownhall/beads · error
db: LabelSQLRepository.Insert: issueID must not be empty
Error message
db: LabelSQLRepository.Insert: issueID must not be empty
What it means
Input validation error from LabelSQLRepository.Insert: the issueID argument is the empty string. The SQL-layer repository refuses to issue an INSERT for a label without knowing which issue it attaches to, failing fast before touching the database.
Source
Thrown at internal/storage/domain/db/label.go:37
}
type labelSQLRepositoryImpl struct {
runner Runner
events domain.EventsSQLRepository
}
var _ domain.LabelSQLRepository = (*labelSQLRepositoryImpl)(nil)
func pickLabelTable(useWisps bool) string {
if useWisps {
return "wisp_labels"
}
return "labels"
}
func (r *labelSQLRepositoryImpl) Insert(ctx context.Context, issueID, label, actor string, opts domain.LabelOpts) error {
if issueID == "" {
return fmt.Errorf("db: LabelSQLRepository.Insert: issueID must not be empty")
}
if label == "" {
return fmt.Errorf("db: LabelSQLRepository.Insert: label must not be empty")
}
// Reject an over-length label before the INSERT IGNORE, which would otherwise
// silently truncate it to the VARCHAR(255) column. This is the proxied-server
// (uow) analog of issueops.AddLabelInTx's guard, so both write stacks return a
// typed ErrFieldTooLong instead of storing a label the caller never sent.
if err := types.CheckFieldLen("label", label); err != nil {
return err
}
table := pickLabelTable(opts.UseWispsTable)
//nolint:gosec // G201: table is one of two hardcoded constants
result, err := r.runner.ExecContext(ctx,
fmt.Sprintf("INSERT IGNORE INTO %s (issue_id, label) VALUES (?, ?)", table),
issueID, label,
)
if err != nil {View on GitHub (pinned to 71377f2769)
Solutions
- Ensure the issue is created and its ID populated before calling Insert
- Fix the caller to pass the real issue ID
- Validate issueID non-empty at your API boundary
- Check for ID propagation bugs in uow/proxy layers
Example fix
// before
repo.Insert(ctx, issue.ID, "bug", actor, opts) // issue.ID == ""
// after
if issue.ID == "" {
return fmt.Errorf("issue not persisted")
}
repo.Insert(ctx, issue.ID, "bug", actor, opts) Defensive patterns
Strategy: validation
Validate before calling
func validateLabelInput(issueID, label string) error {
if issueID == "" { return fmt.Errorf("issueID required") }
if label == "" { return fmt.Errorf("label required") }
return nil
} Type guard
func hasIssueID(i *types.Issue) bool { return i != nil && i.ID != "" } Try / catch
if err := repo.Insert(ctx, issueID, label, actor, opts); err != nil {
if strings.Contains(err.Error(), "issueID must not be empty") {
return fmt.Errorf("cannot label unpersisted issue %q", issueID)
}
return err
} Prevention
- Never attach labels before the issue is created and flushed
- Validate IDs at the API boundary
- Check struct-copy sites that may drop the ID field
- Cover the label-before-create order bug with a regression test
When it happens
Trigger: Calling Insert(ctx, "", label, actor, opts) directly, or via a proxy/uow write stack where the caller's issue was never assigned an ID (e.g. added before creation/flush).
Common situations: Adding a label to a not-yet-persisted issue, an ID field lost in a struct copy, or a proxied-server request missing the issue identifier.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- db: LabelSQLRepository.Insert: label must not be empty
- add label: id must not be empty
- add label: label must not be empty
- remove label: id must not be empty
- remove label: label must not be empty
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/a8d14b030840e208.
Report an issue: GitHub.