gastownhall/beads · error
reparent: %s cannot be its own parent
Error message
reparent: %s cannot be its own parent
What it means
Guard error from reparent: the caller asked to make an issue its own parent (childID == newParentID). A self parent-child edge is invalid in the hierarchy model, so the use case rejects it before any lookup or write. This is analogous to the self-dependency guard in add().
Source
Thrown at internal/storage/domain/dependency.go:385
return fmt.Errorf("remove dep %s -> %s: %w", sourceID, dependsOnID, err)
}
return nil
}
func (u *dependencyUseCaseImpl) Reparent(ctx context.Context, childID, newParentID, actor string) error {
return u.reparent(ctx, childID, newParentID, actor, false)
}
func (u *dependencyUseCaseImpl) ReparentWisp(ctx context.Context, childWispID, newParentID, actor string) error {
return u.reparent(ctx, childWispID, newParentID, actor, true)
}
func (u *dependencyUseCaseImpl) reparent(ctx context.Context, childID, newParentID, actor string, useWisp bool) error {
if childID == "" {
return fmt.Errorf("reparent: childID must not be empty")
}
if childID == newParentID {
return fmt.Errorf("reparent: %s cannot be its own parent", childID)
}
opts := DepInsertOpts{UseWispsTable: useWisp}
res, err := u.depRepo.ListByIssueIDs(ctx, []string{childID}, DepListOpts{
Types: []types.DependencyType{types.DepParentChild},
Direction: DepDirectionOut,
UseWispsTable: useWisp,
})
if err != nil {
return fmt.Errorf("reparent: list current parent: %w", err)
}
// A child can carry MORE THAN ONE parent-child edge — Create accepts
// CreateRequest.ParentID and an explicit parent-child entry in
// Dependencies in the same request — so this is a set replacement, not a
// swap of one edge. Diffing the whole existing set against the target set
// is the same rule the store-backed backends apply in
// issueops.ApplyParentPatch; that body cannot be called from here becauseView on GitHub (pinned to 71377f2769)
Solutions
- Compare childID and newParentID at the call site and skip no-op self-reparents before calling.
- Fix the parent-selection logic so a child is never offered/chosen as its own parent.
- If the intent was 'no change', skip the call entirely rather than passing equal IDs.
- Validate parent choice in scripts: reject any mapping where parent == child.
Example fix
// before
err := uc.Reparent(ctx, childID, parentID, actor)
// after
if childID == parentID {
return nil // no-op reparent
}
err := uc.Reparent(ctx, childID, parentID, actor) Defensive patterns
Strategy: validation
Validate before calling
if childID == newParentID {
return nil // or return a descriptive no-op/validation error
}
err := uc.Reparent(ctx, childID, newParentID, actor) Type guard
func isSelfParent(childID, newParentID string) bool {
return childID != "" && childID == newParentID
} Try / catch
if isSelfParent(childID, newParentID) {
log.Printf("skipping self-parent for %s", childID)
return nil
}
if err := uc.Reparent(ctx, childID, newParentID, actor); err != nil {
return fmt.Errorf("reparent rejected: %w", err)
} Prevention
- Never pre-fill a parent selector with the child's own ID
- Sanitize bulk parent mappings: reject parent == child entries
- Treat self-reparent as a no-op and skip rather than error in scripts
- Mirror the self-dependency rule in any custom UI/form validation
When it happens
Trigger: Reparent(ctx, id, id, actor) or ReparentWisp(ctx, id, id, actor) — typically a UI/form default where the parent dropdown was pre-filled with the child itself, or a script that computes parentID from the same variable as childID.
Common situations: Bulk re-parent scripts mapping issues to 'themselves' as a default fallback; migration code that copies a flat list into parent-child form without excluding self-mappings; user selecting the current issue as its new parent.
Related errors
- db: DependencySQLRepository.ValidateBlockingHierarchy: dep m
- reparent: childID must not be empty
- applyGraph: node %q: planned blocking dependencies create a
- no store is open for this workspace
- not found
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/22baa59f6a262178.
Report an issue: GitHub.