{"record":{"id":"3787da354019796a","repo":"gastownhall/beads","slug":"db-labelsqlrepository-delete-issueid-must-not-be","errorCode":null,"errorMessage":"db: LabelSQLRepository.Delete: issueID must not be empty","messagePattern":"db: LabelSQLRepository\\.Delete: issueID must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/storage/domain/db/label.go","lineNumber":92,"sourceCode":"\t\t}\n\t\treturn nil\n\t}\n\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 {","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/db/label.go#L74-L110","documentation":"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.","triggerScenarios":"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.","commonSituations":"Deserialization failure leaving IssueID empty; calling Delete before assigning fields; wiring bug passing wrong argument position into Delete.","solutions":["Ensure issueID is populated before calling Delete","Add caller-side validation of issueID","Check upstream parsing/deserialization that produced the empty ID"],"exampleFix":"// before\nrepo.Delete(ctx, issueID, label, actor, opts) // issueID may be \"\"\n// after\nif issueID == \"\" { return fmt.Errorf(\"issueID required\") }\nreturn repo.Delete(ctx, issueID, label, actor, opts)","handlingStrategy":"validation","validationCode":"if err := validateIssueID(issueID); err != nil { return err }\n// where\nfunc validateIssueID(id string) error {\n    if id == \"\" { return fmt.Errorf(\"issueID required\") }\n    return nil\n}","typeGuard":"func hasIssueID(i Issue) bool { return i.ID != \"\" }","tryCatchPattern":"if err := repo.Delete(ctx, issueID, label, actor, opts); err != nil {\n    if strings.Contains(err.Error(), \"issueID must not be empty\") { return fmt.Errorf(\"bug: empty issueID passed to Delete\") }\n    return err\n}","preventionTips":["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"],"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"}