{"record":{"id":"8e12768be6fc35a0","repo":"gastownhall/beads","slug":"reparent-add-new-parent-s-w","errorCode":null,"errorMessage":"reparent: add new parent %s: %w","messagePattern":"reparent: add new parent (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/dependency.go","lineNumber":432,"sourceCode":"\t}\n\tif sameStringSet(existing, target) {\n\t\treturn nil\n\t}\n\n\tfor _, oldParentID := range sortedSetDifference(existing, target) {\n\t\tif _, err := u.depRepo.Delete(ctx, childID, oldParentID, actor, opts); err != nil {\n\t\t\treturn fmt.Errorf(\"reparent: remove old parent %s: %w\", oldParentID, err)\n\t\t}\n\t}\n\n\tfor _, addParentID := range sortedSetDifference(target, existing) {\n\t\tdep := &types.Dependency{\n\t\t\tIssueID:     childID,\n\t\t\tDependsOnID: addParentID,\n\t\t\tType:        types.DepParentChild,\n\t\t}\n\t\tif err := u.depRepo.Insert(ctx, dep, actor, opts); err != nil {\n\t\t\treturn fmt.Errorf(\"reparent: add new parent %s: %w\", addParentID, err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc sameStringSet(left, right map[string]struct{}) bool {\n\tif len(left) != len(right) {\n\t\treturn false\n\t}\n\tfor value := range left {\n\t\tif _, ok := right[value]; !ok {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\n// sortedSetDifference returns the members of left absent from right, sorted so","sourceCodeStart":414,"sourceCodeEnd":450,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/dependency.go#L414-L450","documentation":"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.","triggerScenarios":"Calling Reparent or ReparentWisp where sortedSetDifference(target, existing) yields a parent ID and depRepo.Insert for that Dependency returns an error.","commonSituations":"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.","solutions":["Check the wrapped cause for duplicate/constraint violations","Ensure the new parent ID exists before calling Reparent","Retry the reparent — it diffs existing vs target and only adds missing edges","Serialize reparent operations on the same child to avoid races"],"exampleFix":"// before\nif err := u.depRepo.Insert(ctx, dep, actor, opts); err != nil {\n\treturn fmt.Errorf(\"reparent: add new parent %s: %w\", addParentID, err)\n}\n// after\nif err := u.depRepo.Insert(ctx, dep, actor, opts); err != nil && !dberrors.IsAlreadyExists(err) {\n\treturn fmt.Errorf(\"reparent: add new parent %s: %w\", addParentID, err)\n}","handlingStrategy":"try-catch","validationCode":"// ensure target parent exists before reparent\nparent, err := issueRepo.Get(ctx, addParentID)\nif err != nil || parent == nil {\n\treturn fmt.Errorf(\"target parent %s does not exist\", addParentID)\n}","typeGuard":null,"tryCatchPattern":"if err := u.Reparent(ctx, childID, parents, actor, opts); err != nil {\n\tif strings.Contains(err.Error(), \"add new parent\") {\n\t\t// extract the failing parent ID from the message and check duplicates/existence\n\t}\n}","preventionTips":["Pre-validate all target parent IDs exist in the same table (issue vs wisp)","Avoid concurrent reparents on the same child","Retry the idempotent Reparent after transient failures","Match Reparent vs ReparentWisp to the child's table"],"tags":["storage","dependency","reparent","wrapped-error"],"backgroundTag":"dependency-insert-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}