{"record":{"id":"da3bd7069fb13072","repo":"gastownhall/beads","slug":"remove-labels-id-must-not-be-empty","errorCode":null,"errorMessage":"remove labels: id must not be empty","messagePattern":"remove labels: id must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/label.go","lineNumber":127,"sourceCode":"\t\t}\n\t\tif err := u.labelRepo.Insert(ctx, id, label, actor, opts); err != nil {\n\t\t\treturn fmt.Errorf(\"add labels: %s: %w\", label, err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (u *labelUseCaseImpl) RemoveLabels(ctx context.Context, issueID string, labels []string, actor string) error {\n\treturn u.removeMany(ctx, issueID, labels, actor, false)\n}\n\nfunc (u *labelUseCaseImpl) RemoveWispLabels(ctx context.Context, wispID string, labels []string, actor string) error {\n\treturn u.removeMany(ctx, wispID, labels, actor, true)\n}\n\nfunc (u *labelUseCaseImpl) removeMany(ctx context.Context, id string, labels []string, actor string, useWisp bool) error {\n\tif id == \"\" {\n\t\treturn fmt.Errorf(\"remove labels: id must not be empty\")\n\t}\n\topts := LabelOpts{UseWispsTable: useWisp}\n\tfor _, label := range labels {\n\t\tif label == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tif err := u.labelRepo.Delete(ctx, id, label, actor, opts); err != nil {\n\t\t\treturn fmt.Errorf(\"remove labels: %s: %w\", label, err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (u *labelUseCaseImpl) SetLabels(ctx context.Context, issueID string, labels []string, actor string) error {\n\treturn u.setMany(ctx, issueID, labels, actor, false)\n}\n\nfunc (u *labelUseCaseImpl) SetWispLabels(ctx context.Context, wispID string, labels []string, actor string) error {","sourceCodeStart":109,"sourceCodeEnd":145,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/label.go#L109-L145","documentation":"Thrown by removeMany (RemoveLabels / RemoveWispLabels) when the issue or wisp ID is an empty string. Like the add/remove/set family, the ID is validated before touching the repository since deleting labels for an unnamed record is meaningless. Pure input validation.","triggerScenarios":"RemoveLabels(ctx, \"\", labels, actor) or RemoveWispLabels(ctx, \"\", labels, actor); an ID variable empty due to a failed lookup, empty CLI argument, or unset struct field.","commonSituations":"Bulk scripts iterating records where some IDs came back empty; cleanup jobs reading IDs from CSV/JSON with missing fields; using a variable set from a failed parse.","solutions":["Check the ID is non-empty before calling RemoveLabels","Filter out records with empty IDs in bulk operations","Fix the upstream source of the empty ID (failed lookup, missing field)","Ensure you pass the wisp ID to RemoveWispLabels, not an empty issue ID"],"exampleFix":"// before\nstore.RemoveLabels(ctx, id, labels, actor) // id may be \"\"\n// after\nif id == \"\" {\n\treturn fmt.Errorf(\"cannot remove labels: no issue id\")\n}\nstore.RemoveLabels(ctx, id, labels, actor)","handlingStrategy":"validation","validationCode":"if id == \"\" {\n\treturn fmt.Errorf(\"cannot remove labels: issue id is empty\")\n}\nif err := store.RemoveLabels(ctx, id, labels, actor); err != nil { ... }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Guard IDs before calling remove APIs","Filter empty IDs in bulk cleanup scripts","Capture IDs from creation/lookup results, not from user-typed input","Use RemoveWispLabels with wisp IDs, not issue IDs"],"tags":["validation","labels","empty-id"],"backgroundTag":"empty-required-identifier","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}