{"record":{"id":"95e71847a948ddd8","repo":"gastownhall/beads","slug":"db-labelsqlrepository-delete-label-must-not-be-e","errorCode":null,"errorMessage":"db: LabelSQLRepository.Delete: label must not be empty","messagePattern":"db: LabelSQLRepository\\.Delete: label must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/storage/domain/db/label.go","lineNumber":95,"sourceCode":"\tif err := r.events.Record(ctx, domain.Event{\n\t\tIssueID:  issueID,\n\t\tType:     types.EventLabelAdded,\n\t\tActor:    actor,\n\t\tNewValue: label,\n\t}, domain.RecordEventOpts{UseWispsTable: opts.UseWispsTable}); err != nil {\n\t\treturn err\n\t}\n\t// A label is part of the bead snapshot; the idempotent no-op path above\n\t// returns without writing and journals nothing.\n\treturn issueops.RecordEventInTx(ctx, r.runner, issueops.EventUpdate, issueID, actor)\n}\n\nfunc (r *labelSQLRepositoryImpl) Delete(ctx context.Context, issueID, label, actor string, opts domain.LabelOpts) error {\n\tif issueID == \"\" {\n\t\treturn fmt.Errorf(\"db: LabelSQLRepository.Delete: issueID must not be empty\")\n\t}\n\tif label == \"\" {\n\t\treturn fmt.Errorf(\"db: LabelSQLRepository.Delete: label must not be empty\")\n\t}\n\ttable := pickLabelTable(opts.UseWispsTable)\n\t//nolint:gosec // G201: table is one of two hardcoded constants\n\tresult, err := r.runner.ExecContext(ctx,\n\t\tfmt.Sprintf(\"DELETE FROM %s WHERE issue_id = ? AND label = ?\", table),\n\t\tissueID, label,\n\t)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"db: LabelSQLRepository.Delete %s/%s: %w\", issueID, label, err)\n\t}\n\trows, err := result.RowsAffected()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"db: LabelSQLRepository.Delete %s/%s: rows affected: %w\", issueID, label, err)\n\t}\n\tif rows == 0 {\n\t\treturn nil\n\t}\n\tif err := r.events.Record(ctx, domain.Event{","sourceCodeStart":77,"sourceCodeEnd":113,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/label.go#L77-L113","documentation":"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.","triggerScenarios":"Calling Delete(ctx, issueID, \"\", actor, opts) — e.g. a label variable lost in string processing, or user input stripped to empty by sanitization.","commonSituations":"Trimmed user input that was only whitespace; label field never set on a struct before persistence; parsing bug splitting labels on the wrong delimiter.","solutions":["Ensure label is non-empty before calling Delete","Validate/sanitize label input at the CLI/UI boundary","Check string-splitting logic that produced an empty label"],"exampleFix":"// before\nlabel := strings.TrimSpace(userLabel)\nrepo.Delete(ctx, issueID, label, actor, opts)\n// after\nlabel := strings.TrimSpace(userLabel)\nif label == \"\" { return fmt.Errorf(\"label required\") }\nrepo.Delete(ctx, issueID, label, actor, opts)","handlingStrategy":"validation","validationCode":"label = strings.TrimSpace(rawLabel)\nif label == \"\" { return fmt.Errorf(\"label must not be empty\") }","typeGuard":null,"tryCatchPattern":"if err := repo.Delete(ctx, issueID, label, actor, opts); err != nil {\n    if strings.Contains(err.Error(), \"label must not be empty\") { return fmt.Errorf(\"cannot remove empty label for %s\", issueID) }\n    return err\n}","preventionTips":["Trim and validate label input before repository calls","Reject whitespace-only labels at the UI/CLI layer","Check label-parsing/splitting logic for empty tokens","Test Delete with empty and whitespace labels"],"tags":["validation","empty-input","repository"],"backgroundTag":"empty-required-argument","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}