gastownhall/beads · error
add label '%s' on %s: %w
Error message
add label '%s' on %s: %w
What it means
Wraps a failure from AddLabel or AddWispLabel when propagating a label from a parent issue to one of its children. The fan-out loop applies the label per child; if any child write fails, the error identifies the label, the child ID, and the root cause via %w. Note the partial-failure property: earlier children may already have the label.
Source
Thrown at cmd/bd/label_proxied_server.go:189
// ga-2ltro.12 deleted from `bd tag`, `bd set-state` and the molecule
// port — the branch a front door can get backwards and put a wisp's
// label in the durable table. It survives because propagate is a search
// plus a fan-out that must land as ONE transaction over N children, and
// a per-child Lifecycle.Update is N transactions: the atomicity this
// command has today would be the price of the migration. The shape that
// keeps both is issueops.BatchApplier.ApplyBatch with one ItemUpdate per
// child carrying this same label patch, and it is blocked on a cmd/bd
// accessor for that role. That is the follow-up this waiver names
// (ga-2ltro.12); it is the last one on this list that WRITES.
for _, child := range children {
var e error
if child.Ephemeral {
e = uw.LabelUseCase().AddWispLabel(ctx, child.ID, label, actor) //nolint:forbidigo // atomic N-child fan-out; awaits a BatchApplier accessor
} else {
e = uw.LabelUseCase().AddLabel(ctx, child.ID, label, actor) //nolint:forbidigo // atomic N-child fan-out; awaits a BatchApplier accessor
}
if e != nil {
return "", fmt.Errorf("add label '%s' on %s: %w", label, child.ID, e)
}
}
return fmt.Sprintf("bd: propagate label '%s' from %s to %d children", label, parentID, len(children)), nil
})
if err != nil {
return HandleErrorRespectJSON("label propagate: %v", err)
}
if len(children) == 0 {
if jsonOutput {
return outputJSON([]map[string]interface{}{})
}
fmt.Printf("No children found for %s\n", parentID)
return nil
}
commandDidWrite.Store(true)
if jsonOutput {View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped error to see which child ID failed and why
- Fix the storage issue, then re-run propagation — label application is idempotent so already-labeled children are safe
- If a child was deleted, refresh the parent's children list before retrying
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure the child still exists before fanning out
for _, child := range children {
if _, err := uw.IssueUseCase().GetIssue(ctx, child.ID); err != nil {
return fmt.Errorf("child %s missing: %w", child.ID, err)
}
} Type guard
func isNotFound(err error) bool { return errors.Is(err, storage.ErrNotFound) } Try / catch
if e := uw.LabelUseCase().AddLabel(ctx, child.ID, label, actor); e != nil {
if isNotFound(e) { continue } // child vanished; skip
return fmt.Errorf("add label '%s' on %s: %w", label, child.ID, e)
} Prevention
- Run label propagation serially per workspace to avoid lock contention
- Treat fan-out as idempotent and safe to re-run after partial failure
- Refresh the children list immediately before propagation to avoid stale IDs
When it happens
Trigger: Running label propagation where child.Ephemeral routes to AddWispLabel (or AddLabel for normal issues) and that use-case call returns an error — storage write failure, concurrent modification, or the child issue no longer existing.
Common situations: Database locked/unavailable during a multi-child fan-out; child deleted between the SearchIssues listing and the write; permission or actor validation failures on AddLabel.
Related errors
- add label '%s' on %s: %w
- read labels for %s: %w
- copy label %q for %s: %w
- failed to search protos: %w
- iter issues: hydrate labels: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/e2722b68b0cff2da.
Report an issue: GitHub.