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
- Inspect the wrapped %w cause to identify the repository-level failure
- Retry the reparent once the DB/context issue is resolved (it is idempotent: already-removed edges are skipped)
- Verify the child and parent IDs exist in the expected table (issues vs wisps)
- 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
- Reuse Reparent's idempotence: retry the whole call instead of manual edge surgery
- Keep reparent calls serialized per child ID
- Pass a context with a generous deadline
- Log oldParentID from the message to target repairs
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
- reparent: add new parent %s: %w
- GetForIssueIDs: %w
- GetForIssueIDs (wisps): %w
- list dep metadata: %w
- iter dep metadata: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/83516a10456735f3.
Report an issue: GitHub.