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

  1. Read the wrapped inner error to identify the failing label and destination driver cause.
  2. Close other bd sessions holding a lock on the destination database.
  3. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/c56c43ec0cc0b4f5. Report an issue: GitHub.