{"record":{"id":"7c41203d58b5c2c4","repo":"gastownhall/beads","slug":"failed-to-check-blocker-ancestry-w","errorCode":null,"errorMessage":"failed to check blocker ancestry: %w","messagePattern":"failed to check blocker ancestry: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependencies.go","lineNumber":532,"sourceCode":"\treturn []string{\"dependencies\", \"wisp_dependencies\"}\n}\n\n// CheckBlockingHierarchyInTx rejects blocking dependencies between an issue\n// and its own ancestor or descendant. Cross-prefix/external targets must be\n// filtered by the caller because no local hierarchy can connect them.\nfunc CheckBlockingHierarchyInTx(ctx context.Context, tx DBTX, dep *types.Dependency, depTables []string) error {\n\tif dep.Type != types.DepBlocks && dep.Type != types.DepConditionalBlocks {\n\t\treturn nil\n\t}\n\tif dep.IssueID == dep.DependsOnID {\n\t\treturn nil // The dedicated self-dependency check owns this error.\n\t}\n\tif len(depTables) == 0 {\n\t\tdepTables = cycleDetectionTables()\n\t}\n\tblockerIsAncestor, err := isAncestorInTx(ctx, tx, dep.IssueID, dep.DependsOnID, depTables)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to check blocker ancestry: %w\", err)\n\t}\n\tif blockerIsAncestor {\n\t\treturn &domain.DependencyHierarchyConflictError{\n\t\t\tIssueID: dep.IssueID, BlockerID: dep.DependsOnID, BlockerIsAncestor: true,\n\t\t}\n\t}\n\tblockerIsDescendant, err := isAncestorInTx(ctx, tx, dep.DependsOnID, dep.IssueID, depTables)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to check blocker ancestry: %w\", err)\n\t}\n\tif blockerIsDescendant {\n\t\treturn &domain.DependencyHierarchyConflictError{\n\t\t\tIssueID: dep.IssueID, BlockerID: dep.DependsOnID,\n\t\t}\n\t}\n\treturn nil\n}\n","sourceCodeStart":514,"sourceCodeEnd":550,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependencies.go#L514-L550","documentation":"Wraps an error from isAncestorInTx while CheckBlockingHierarchyInTx checks whether the blocker is an ancestor of the dependent issue in the hierarchy. This is not the hierarchy-conflict result itself (that is a DependencyHierarchyConflictError); it means the ancestry lookup query failed, so the hierarchy validation could not complete.","triggerScenarios":"CheckBlockingHierarchyInTx invoked (via addDependencyInTx, PersistDependenciesWithOptionsResult, or runEndGate) with empty depTables defaulted to cycleDetectionTables, and isAncestorInTx errors on its traversal — DB failure, aborted tx, or corrupted hierarchy rows.","commonSituations":"Database connectivity loss during dep-add or an end-gate run; concurrent renames of issues invalidating traversal mid-tx; malformed parent links in the issues table.","solutions":["Inspect the wrapped cause for the underlying SQL error and address it","Retry the operation; the ancestry check is read-only and the tx rolls back cleanly","Validate parent chains of both issues (no dangling parent IDs) before adding the dependency","Check for lock contention from concurrent hierarchy mutations and serialize writes"],"exampleFix":"// before\nif err := issueops.CheckBlockingHierarchyInTx(ctx, tx, dep, nil); err != nil {\n\treturn err\n}\n// after\nif err := issueops.CheckBlockingHierarchyInTx(ctx, tx, dep, nil); err != nil {\n\tvar hier *domain.DependencyHierarchyConflictError\n\tif errors.As(err, &hier) {\n\t\treturn err // genuine hierarchy conflict\n\t}\n\treturn fmt.Errorf(\"hierarchy check unavailable, deferring dep add: %w\", err)\n}","handlingStrategy":"try-catch","validationCode":"// ensure both issues have resolvable parent chains before hierarchy check\nif danglingParent(ctx, db, dep.IssueID) || danglingParent(ctx, db, dep.DependsOnID) {\n\treturn errors.New(\"cannot validate hierarchy: dangling parent reference\")\n}","typeGuard":"func isAncestryInfraFailure(err error) bool {\n\tvar hier *domain.DependencyHierarchyConflictError\n\treturn err != nil && !errors.As(err, &hier) &&\n\t\tstrings.Contains(err.Error(), \"failed to check blocker ancestry\")\n}","tryCatchPattern":"err := issueops.CheckBlockingHierarchyInTx(ctx, tx, dep, nil)\nvar hier *domain.DependencyHierarchyConflictError\nswitch {\ncase errors.As(err, &hier):\n\treturn err // genuine hierarchy conflict\ncase err != nil:\n\treturn retryWithBackoff(op) // ancestry lookup failed, not a conflict\n}","preventionTips":["Use errors.As with DependencyHierarchyConflictError to separate real conflicts from infra failures","Keep parent links free of dangling IDs","Retry after transient DB failures; the tx rolls back cleanly","Serialize hierarchy mutations to avoid traversal races"],"tags":["database","hierarchy","transaction"],"backgroundTag":"blocker-ancestry-check-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}