gastownhall/beads · error

reparent: remove old parent %s: %w

Error message

reparent: remove old parent %s: %w

What it means

Wraps a failure from depRepo.Delete while reparenting removes an old parent-child dependency edge. The child's existing parent set is being diffed against the target set and one of the removals failed at the repository layer. The wrap preserves the underlying cause (e.g. DB error, context cancellation).

Source

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

	// issueops.ApplyParentPatch; that body cannot be called from here because
	// internal/storage/issueops imports this package (bd-yby99.26).
	existing := map[string]struct{}{}
	for _, dep := range res.Outgoing[childID] {
		if dep.Type == types.DepParentChild {
			existing[dep.DependsOnID] = struct{}{}
		}
	}
	target := map[string]struct{}{}
	if newParentID != "" {
		target[newParentID] = struct{}{}
	}
	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) {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped %w cause to identify the repository-level failure
  2. Retry the reparent once the DB/context issue is resolved (it is idempotent: already-removed edges are skipped)
  3. Verify the child and parent IDs exist in the expected table (issues vs wisps)
  4. Check context deadline/cancellation propagation

Example fix

// before
if _, err := u.depRepo.Delete(ctx, childID, oldParentID, actor, opts); err != nil {
	return fmt.Errorf("reparent: remove old parent %s: %w", oldParentID, err)
}
// after (survive transient removal issues, verify at end)
if _, err := u.depRepo.Delete(ctx, childID, oldParentID, actor, opts); err != nil && !dberrors.IsNotExist(err) {
	return fmt.Errorf("reparent: remove old parent %s: %w", oldParentID, err)
}
Defensive patterns

Strategy: retry

Validate before calling

// verify both IDs are non-empty and the edge exists
if childID == "" || oldParentID == "" {
	return fmt.Errorf("child and old parent IDs required")
}
deps, _ := u.GetForIssueIDs(ctx, []string{childID})

Try / catch

if err := u.Reparent(ctx, childID, newParents, actor, opts); err != nil {
	var wrapped interface{ Unwrap() error }
	if errors.As(err, &target) { /* inspect root cause */ }
	// reparent is idempotent; safe to retry
}

Prevention

When it happens

Trigger: Calling Reparent or ReparentWisp where sortedSetDifference(existing, target) yields an old parent ID and depRepo.Delete returns an error for (childID, oldParentID).

Common situations: Database connectivity loss mid-reparent; context cancelled or timed out during the batch; storage-level constraint or concurrent modification of the same dependency rows.

Related errors


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