gastownhall/beads · error

reparent: add new parent %s: %w

Error message

reparent: add new parent %s: %w

What it means

Wraps a failure from depRepo.Insert while reparenting adds a new parent-child edge of type DepParentChild. The insert of one target parent failed after prior steps may have already run, so partial reparent state is possible. The underlying repository error is preserved via %w.

Source

Thrown at internal/storage/domain/dependency.go:432

	}
	if sameStringSet(existing, target) {
		return nil
	}

	for _, oldParentID := range sortedSetDifference(existing, target) {
		if _, err := u.depRepo.Delete(ctx, childID, oldParentID, actor, opts); err != nil {
			return fmt.Errorf("reparent: remove old parent %s: %w", oldParentID, err)
		}
	}

	for _, addParentID := range sortedSetDifference(target, existing) {
		dep := &types.Dependency{
			IssueID:     childID,
			DependsOnID: addParentID,
			Type:        types.DepParentChild,
		}
		if err := u.depRepo.Insert(ctx, dep, actor, opts); err != nil {
			return fmt.Errorf("reparent: add new parent %s: %w", addParentID, err)
		}
	}
	return nil
}

func sameStringSet(left, right map[string]struct{}) bool {
	if len(left) != len(right) {
		return false
	}
	for value := range left {
		if _, ok := right[value]; !ok {
			return false
		}
	}
	return true
}

// sortedSetDifference returns the members of left absent from right, sorted so

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped cause for duplicate/constraint violations
  2. Ensure the new parent ID exists before calling Reparent
  3. Retry the reparent — it diffs existing vs target and only adds missing edges
  4. Serialize reparent operations on the same child to avoid races

Example fix

// before
if err := u.depRepo.Insert(ctx, dep, actor, opts); err != nil {
	return fmt.Errorf("reparent: add new parent %s: %w", addParentID, err)
}
// after
if err := u.depRepo.Insert(ctx, dep, actor, opts); err != nil && !dberrors.IsAlreadyExists(err) {
	return fmt.Errorf("reparent: add new parent %s: %w", addParentID, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure target parent exists before reparent
parent, err := issueRepo.Get(ctx, addParentID)
if err != nil || parent == nil {
	return fmt.Errorf("target parent %s does not exist", addParentID)
}

Try / catch

if err := u.Reparent(ctx, childID, parents, actor, opts); err != nil {
	if strings.Contains(err.Error(), "add new parent") {
		// extract the failing parent ID from the message and check duplicates/existence
	}
}

Prevention

When it happens

Trigger: Calling Reparent or ReparentWisp where sortedSetDifference(target, existing) yields a parent ID and depRepo.Insert for that Dependency returns an error.

Common situations: Duplicate parent edge inserted concurrently by another actor; referential integrity failure because the target parent ID does not exist; DB write failure or context timeout.

Related errors


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