gastownhall/beads · error
set labels: remove %s: %w
Error message
set labels: remove %s: %w
What it means
Wraps a failure from labelRepo.Delete during the removal half of setMany (SetLabels / SetWispLabels). When the current label set is being pruned toward the desired set, deleting a no-longer-wanted label failed. The offending label is named; changes made before the failure are not rolled back, so the label set may be partially updated.
Source
Thrown at internal/storage/domain/label.go:169
return fmt.Errorf("set labels: id must not be empty")
}
opts := LabelOpts{UseWispsTable: useWisp}
current, err := u.labelRepo.List(ctx, id, opts)
if err != nil {
return fmt.Errorf("set labels: list current: %w", err)
}
desired := make(map[string]bool, len(labels))
for _, l := range labels {
if l != "" {
desired[l] = true
}
}
existing := make(map[string]bool, len(current))
for _, l := range current {
existing[l] = true
if !desired[l] {
if err := u.labelRepo.Delete(ctx, id, l, actor, opts); err != nil {
return fmt.Errorf("set labels: remove %s: %w", l, err)
}
}
}
for l := range desired {
if !existing[l] {
if err := u.labelRepo.Insert(ctx, id, l, actor, opts); err != nil {
return fmt.Errorf("set labels: add %s: %w", l, err)
}
}
}
return nil
}
func (u *labelUseCaseImpl) GetLabels(ctx context.Context, issueID string) ([]string, error) {
return u.list(ctx, issueID, false)
}
func (u *labelUseCaseImpl) GetWispLabels(ctx context.Context, wispID string) ([]string, error) {View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped error and the named label to identify the failing delete
- Re-run SetLabels idempotently — it recomputes the diff, so a retry completes remaining removals
- Check for concurrent writers (locks, other agents) and serialize label mutations
- Verify the ID/table type matches (issue vs wisp APIs)
Example fix
// before
if err := store.SetLabels(ctx, id, desired, actor); err != nil {
log.Fatal(err) // gives up on partial state
}
// after
if err := store.SetLabels(ctx, id, desired, actor); err != nil {
time.Sleep(backoff)
return store.SetLabels(ctx, id, desired, actor) // recomputes diff
} Defensive patterns
Strategy: retry
Validate before calling
current, err := store.GetLabels(ctx, id)
if err != nil { return err }
// Pre-check that removals are actually needed
if len(current) == 0 { return nil } Try / catch
if err := store.SetLabels(ctx, id, desired, actor); err != nil {
log.Printf("set labels partially applied on %s: %v — retrying", id, err)
time.Sleep(backoff)
return store.SetLabels(ctx, id, desired, actor) // recomputes diff
} Prevention
- Retry SetLabels on failure — the recomputed diff completes pending removals
- Serialize concurrent writers on the same issue to avoid mid-diff conflicts
- Check the named label in the message to diagnose which delete failed
- Reconcile with GetLabels after persistent failures
When it happens
Trigger: SetLabels where the desired set omits existing labels and Delete of one of them fails: driver error, concurrent modification, missing record, constraint.
Common situations: Two agents running SetLabels concurrently on the same issue; connectivity drop mid-diff-apply; wisp/issue table mismatch causing delete against the wrong table.
Related errors
- remove labels: %s: %w
- set labels: add %s: %w
- add label '%s' on %s: %w
- add label '%s' on %s: %w
- read labels for %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/9ef96616ba7ef889.
Report an issue: GitHub.