{"record":{"id":"83b3d2a13f2bff9e","repo":"gastownhall/beads","slug":"add-labels-s-w","errorCode":null,"errorMessage":"add labels: %s: %w","messagePattern":"add labels: (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/label.go","lineNumber":111,"sourceCode":"func (u *labelUseCaseImpl) AddLabels(ctx context.Context, issueID string, labels []string, actor string) error {\n\treturn u.addMany(ctx, issueID, labels, actor, false)\n}\n\nfunc (u *labelUseCaseImpl) AddWispLabels(ctx context.Context, wispID string, labels []string, actor string) error {\n\treturn u.addMany(ctx, wispID, labels, actor, true)\n}\n\nfunc (u *labelUseCaseImpl) addMany(ctx context.Context, id string, labels []string, actor string, useWisp bool) error {\n\tif id == \"\" {\n\t\treturn fmt.Errorf(\"add 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.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}","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/label.go#L93-L129","documentation":"Wraps an error returned by labelRepo.Insert while adding a single label via addMany (AddLabels / AddWispLabels). The message names the label that failed so callers can tell which item in the batch broke. Underlying causes come from the storage layer (record missing, constraint violation, driver error).","triggerScenarios":"AddLabels or AddWispLabels called with a non-empty label where the labelRepo.Insert call fails: the issue/wisp ID does not exist in the table, a database constraint fires, or the storage driver errors.","commonSituations":"Adding labels to an issue ID that was deleted or from another database; duplicated label violating a uniqueness constraint; Dolt/driver connection problems; using a wisp ID with the non-wisp API.","solutions":["Read the wrapped error (%w) for the root storage cause and fix accordingly","Confirm the issue/wisp ID exists (GetIssue/GetLabels) before adding labels","Check for duplicate labels if a uniqueness constraint is reported","Verify database connectivity and that the correct database/migration state is present"],"exampleFix":"// before\nif err := store.AddLabels(ctx, id, labels, actor); err != nil {\n\tfmt.Println(err) // opaque\n}\n// after\nif err := store.AddLabels(ctx, id, labels, actor); err != nil {\n\tif _, gerr := store.GetIssue(ctx, id); gerr != nil {\n\t\treturn fmt.Errorf(\"issue %s missing: %w\", id, gerr)\n\t}\n\treturn err\n}","handlingStrategy":"try-catch","validationCode":"current, err := store.GetLabels(ctx, id)\nif err != nil { return err }\nnewLabels := slices.DeleteFunc(slices.Clone(labels), func(l string) bool {\n\treturn l == \"\" || slices.Contains(current, l)\n})","typeGuard":null,"tryCatchPattern":"if err := store.AddLabels(ctx, id, labels, actor); err != nil {\n\tvar label string\n\tif _, perr := fmt.Sscanf(err.Error(), \"add labels: %s\", &label); perr == nil {\n\t\tlog.Printf(\"failed adding label %q to %s: %v\", label, id, err)\n\t} else {\n\t\treturn err\n\t}\n}","preventionTips":["Confirm the issue exists before adding labels","Dedupe and drop empty labels before calling","Treat label adds as idempotent (skip already-present labels)","Keep database connectivity and migrations healthy"],"tags":["labels","storage","insert-failed"],"backgroundTag":"label-insert-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}