{"record":{"id":"325e648408aaabd2","repo":"gastownhall/beads","slug":"add-dep-hierarchy-check-w","errorCode":null,"errorMessage":"add dep: hierarchy check: %w","messagePattern":"add dep: hierarchy check: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/dependency.go","lineNumber":291,"sourceCode":"\t\treturn fmt.Errorf(\"add dep: dep must not be nil\")\n\t}\n\tif dep.IssueID == \"\" || dep.DependsOnID == \"\" {\n\t\treturn fmt.Errorf(\"add dep: IssueID and DependsOnID must be non-empty\")\n\t}\n\n\t// Self-dependency guard mirrors issueops.CheckDependencyCycleInTx: it is\n\t// checked BEFORE the cycle probe and for ALL dep types, and emits the\n\t// dedicated self-dep message. A blocking self-edge otherwise trips HasCycle\n\t// and would report the wrong (cycle) error (#4547 F-1).\n\tif dep.IssueID == dep.DependsOnID {\n\t\treturn fmt.Errorf(\"%w: %s cannot depend on itself\", ErrSelfDependency, dep.IssueID)\n\t}\n\tif err := u.depRepo.ValidateBlockingHierarchy(ctx, dep); err != nil {\n\t\tvar hierarchyConflict *DependencyHierarchyConflictError\n\t\tif errors.As(err, &hierarchyConflict) {\n\t\t\treturn err\n\t\t}\n\t\treturn fmt.Errorf(\"add dep: hierarchy check: %w\", err)\n\t}\n\n\tif types.IsSchedulingEdge(dep.Type) {\n\t\tcycle, err := u.depRepo.HasCycle(ctx, dep.IssueID, dep.DependsOnID)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"add dep: cycle check: %w\", err)\n\t\t}\n\t\tif cycle {\n\t\t\t// Match the embedded store's user-facing wording verbatim (no ids\n\t\t\t// prefix) so gc code that string-matches this error behaves the same\n\t\t\t// on both plumbings (#4547 F-1).\n\t\t\treturn ErrDependencyCycle\n\t\t}\n\t}\n\n\tif err := u.depRepo.Insert(ctx, dep, actor, DepInsertOpts{UseWispsTable: useWisp, HierarchyValidated: true, CycleValidated: true, EmitEvent: true}); err != nil {\n\t\t// The retype conflict is a user-facing error whose message already\n\t\t// matches embedded verbatim; pass it through unwrapped so the CLI does","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/dependency.go#L273-L309","documentation":"This error wraps a failure from depRepo.ValidateBlockingHierarchy during dependency creation. The hierarchy check verifies blocking-dependency constraints; only recognized *DependencyHierarchyConflictError values are passed through as-is — any other repository error (SQL failure, context cancellation, unexpected backend state) is wrapped as 'add dep: hierarchy check:'.","triggerScenarios":"Calling AddDependency/AddWispDependency (with a non-scheduling or any dep reaching the check) where ValidateBlockingHierarchy fails with a non-hierarchy-conflict error: database query failure, connection loss, cancelled context, or an unexpected repository error inside the hierarchy validation.","commonSituations":"Database unavailable or timing out during hierarchy traversal; corrupted hierarchy data causing the validation query to error; context deadline exceeded while validating deep blocking chains.","solutions":["Read the wrapped cause after 'hierarchy check:' for the repository/driver error","Retry on transient connection/deadline errors","If the cause is a *DependencyHierarchyConflictError it is returned unwrapped — handle that separately as a domain conflict","Verify hierarchy-related tables are intact (bd doctor / schema check) if errors persist"],"exampleFix":"// before\nerr := uc.AddDependency(ctx, dep, actor)\nif err != nil { return err }\n// after\nerr := uc.AddDependency(ctx, dep, actor)\nvar conflict *storage.DependencyHierarchyConflictError\nif errors.As(err, &conflict) { return fmt.Errorf(\"hierarchy conflict: %s\", conflict.Error()) }\nif isTransient(err) { retry(...) }\nreturn err","handlingStrategy":"try-catch","validationCode":"ctx, cancel := context.WithTimeout(ctx, 30*time.Second)\ndefer cancel() // hierarchy traversal can be slow; avoid deadline surprises\nif err := pingDB(ctx); err != nil { return err }","typeGuard":"func isHierarchyConflict(err error) bool {\n    var hce *storage.DependencyHierarchyConflictError\n    return errors.As(err, &hce)\n}","tryCatchPattern":"err := uc.AddDependency(ctx, dep, actor)\nif err != nil {\n    var hce *storage.DependencyHierarchyConflictError\n    if errors.As(err, &hce) { return err } // domain conflict, surface as-is\n    if isTransient(err) { return retryAdd(ctx, dep, actor) }\n    return fmt.Errorf(\"could not add dependency: %w\", err)\n}","preventionTips":["Check DB health before bulk dependency operations","Distinguish hierarchy conflicts (expected) from infrastructure errors via errors.As","Use generous timeouts for deep blocking-hierarchy validations"],"tags":["dependency","database","hierarchy","validation"],"backgroundTag":"hierarchy-check-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}