{"record":{"id":"b549cc6c24403a50","repo":"gastownhall/beads","slug":"set-labels-list-current-w","errorCode":null,"errorMessage":"set labels: list current: %w","messagePattern":"set labels: list current: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/label.go","lineNumber":156,"sourceCode":"\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 {\n\treturn u.setMany(ctx, wispID, labels, actor, true)\n}\n\nfunc (u *labelUseCaseImpl) setMany(ctx context.Context, id string, labels []string, actor string, useWisp bool) error {\n\tif id == \"\" {\n\t\treturn fmt.Errorf(\"set labels: id must not be empty\")\n\t}\n\topts := LabelOpts{UseWispsTable: useWisp}\n\tcurrent, err := u.labelRepo.List(ctx, id, opts)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"set labels: list current: %w\", err)\n\t}\n\tdesired := make(map[string]bool, len(labels))\n\tfor _, l := range labels {\n\t\tif l != \"\" {\n\t\t\tdesired[l] = true\n\t\t}\n\t}\n\texisting := make(map[string]bool, len(current))\n\tfor _, l := range current {\n\t\texisting[l] = true\n\t\tif !desired[l] {\n\t\t\tif err := u.labelRepo.Delete(ctx, id, l, actor, opts); err != nil {\n\t\t\t\treturn fmt.Errorf(\"set labels: remove %s: %w\", l, err)\n\t\t\t}\n\t\t}\n\t}\n\tfor l := range desired {\n\t\tif !existing[l] {","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/label.go#L138-L174","documentation":"Wraps a failure from labelRepo.List inside setMany (SetLabels / SetWispLabels). SetLabels must read the current labels to compute the diff before mutating; if that read fails, the whole set operation is aborted with no changes made. The error preserves the underlying cause via %w.","triggerScenarios":"SetLabels/SetWispLabels called on a valid ID but labelRepo.List fails: storage driver error, database unavailable, table missing/mis-migrated, or transaction conflict.","commonSituations":"Dolt/driver connectivity problems; running against a database created before labels tables existed (migration missing); transient conflicts while other agents write concurrently.","solutions":["Inspect the wrapped error for the storage root cause","Verify database connectivity and that migrations have run","Retry the operation — the read failed before any mutation, so it is safe to retry","Confirm you are pointed at the correct database (right repo/dolt remote)"],"exampleFix":"// before\nerr := store.SetLabels(ctx, id, desired, actor) // opaque\n// after\nif err := store.SetLabels(ctx, id, desired, actor); err != nil {\n\tif errors.Is(err, driverConnErr) { time.Sleep(retryBackoff); err = store.SetLabels(ctx, id, desired, actor) }\n\treturn err\n}","handlingStrategy":"retry","validationCode":"// No caller-side validation applies; ensure connectivity before the call.\nif _, err := store.GetLabels(ctx, id); err != nil {\n\treturn fmt.Errorf(\"storage unavailable before SetLabels: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"err := store.SetLabels(ctx, id, desired, actor)\nfor i := 0; err != nil && i < 3; i++ {\n\ttime.Sleep(time.Duration(1<<i) * 100 * time.Millisecond)\n\terr = store.SetLabels(ctx, id, desired, actor) // safe: read failed before mutation\n}","preventionTips":["Verify database connectivity and migrations before bulk label operations","Retry SetLabels on transient storage errors — it is diff-based and safe to re-run","Confirm the correct database/remote is in use","Handle driver errors at one boundary instead of scattering retries"],"tags":["labels","storage","list-failed","database"],"backgroundTag":"label-list-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}