gastownhall/beads · error
copy label %q for %s: %w
Error message
copy label %q for %s: %w
What it means
After reading labels, copyIssueRelations writes each label to the destination via dst.AddLabel. If any single label write fails, the migration aborts with this error naming the label and issue.
Source
Thrown at cmd/bd/migrate_personal.go:211
pluralIssue(len(personal)),
ui.RenderAccent(planningPath))
return nil
}
// copyIssueRelations copies the labels, dependency records, and comments for a
// single issue from src to dst. The issue row itself must already exist in dst
// (created in phase 1a of runMigratePersonal). Every step — including the reads
// from src — is fatal on error so the caller can abort the migration before any
// source data is deleted; a partial copy must never be mistaken for success
// (maphew review, be-e2nb).
func copyIssueRelations(ctx context.Context, src, dst storage.DoltStorage, issue *types.Issue, actor string) error {
labels, err := src.GetLabels(ctx, issue.ID)
if err != nil {
return fmt.Errorf("read labels for %s: %w", issue.ID, err)
}
for _, label := range labels {
if err := dst.AddLabel(ctx, issue.ID, label, actor); err != nil {
return fmt.Errorf("copy label %q for %s: %w", label, issue.ID, err)
}
}
deps, err := src.GetDependencyRecords(ctx, issue.ID)
if err != nil {
return fmt.Errorf("read dependencies for %s: %w", issue.ID, err)
}
for _, dep := range deps {
if err := dst.AddDependency(ctx, dep, actor); err != nil {
return fmt.Errorf("copy dependency %s→%s: %w", dep.IssueID, dep.DependsOnID, err)
}
}
comments, err := src.GetIssueComments(ctx, issue.ID)
if err != nil {
return fmt.Errorf("read comments for %s: %w", issue.ID, err)
}
for _, c := range comments {View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped inner error to identify the failing label and destination driver cause.
- Close other bd sessions holding a lock on the destination database.
- If duplicates are the cause, clear the partially migrated destination or run migration into a fresh destination, then retry.
Defensive patterns
Strategy: try-catch
Validate before calling
labels, err := src.GetLabels(ctx, issue.ID)
if err != nil { return err }
_ = labels // source readable; destination lock still possible Try / catch
if err := copyIssueRelations(ctx, src, dst, issue, actor); err != nil {
// err contains: copy label "x" for <id>: <driver err>
if errors.Is(err, storage.ErrLocked) { /* retry after releasing lock */ }
return err
} Prevention
- Ensure no concurrent bd session holds the destination DB.
- Migrate into a fresh/empty destination to avoid duplicate-label conflicts.
- Check the wrapped error's label name to pinpoint the failing row.
When it happens
Trigger: dst.AddLabel(ctx, issue.ID, label, actor) errors — destination DB locked, duplicate label constraint, or driver write failure while copying a specific label.
Common situations: Destination .beads DB is open in another bd process (lock conflict), or a prior partial migration left the label already present with a uniqueness constraint.
Related errors
- read labels for %s: %w
- read dependencies for %s: %w
- copy dependency %s→%s: %w
- read comments for %s: %w
- copy comment for %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/c56c43ec0cc0b4f5.
Report an issue: GitHub.