{"record":{"id":"1eda4e2b0da0c3ff","repo":"gastownhall/beads","slug":"add-deps-final-cycle-check-w","errorCode":null,"errorMessage":"add deps: final cycle check: %w","messagePattern":"add deps: final cycle check: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/dependency.go","lineNumber":776,"sourceCode":"\t\t\t\tvar missingEndpoint *DependencyEndpointNotFoundError\n\t\t\t\tif errors.As(err, &missingEndpoint) {\n\t\t\t\t\treturn BulkAddDepsResult{}, err\n\t\t\t\t}\n\t\t\t\treturn BulkAddDepsResult{}, fmt.Errorf(\"add deps[%d]: insert: %w\", i, err)\n\t\t\t}\n\t\t}\n\t}\n\tvar pairs [][2]string\n\tfor _, dep := range deps {\n\t\tif !types.IsSchedulingEdge(dep.Type) {\n\t\t\tcontinue\n\t\t}\n\t\tpairs = append(pairs, [2]string{dep.IssueID, dep.DependsOnID})\n\t}\n\tif len(pairs) > 0 {\n\t\tcyclePath, err := u.depRepo.CycleThroughEdges(ctx, pairs)\n\t\tif err != nil {\n\t\t\treturn BulkAddDepsResult{}, fmt.Errorf(\"add deps: final cycle check: %w\", err)\n\t\t}\n\t\tif cyclePath != \"\" {\n\t\t\treturn BulkAddDepsResult{}, cycleErrorf(\"add deps: dependency cycle would be created: %s\", cyclePath)\n\t\t}\n\t}\n\treturn BulkAddDepsResult{Added: deps}, nil\n}\n\n// ValidateBlockingHierarchy passes the edge straight to the repository check\n// AddDependencies runs per edge, so a caller re-running the gate at the end of\n// a mixed request raises the identical *DependencyHierarchyConflictError rather\n// than a second opinion about the same graph.\nfunc (u *dependencyUseCaseImpl) ValidateBlockingHierarchy(ctx context.Context, dep *types.Dependency) error {\n\treturn u.depRepo.ValidateBlockingHierarchy(ctx, dep)\n}\n\n// CycleThroughEdges passes the pairs straight to the repository walk\n// AddDependencies runs as its own final gate. The caller composes the refusal,","sourceCodeStart":758,"sourceCodeEnd":794,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/dependency.go#L758-L794","documentation":"After inserting edges individually, AddDependencies performs one combined CycleThroughEdges check over all non-scheduling pairs to catch cycles that only appear when the whole batch is considered. This error wraps a failure of that final query; a detected cycle instead yields the cycleErrorf message 'dependency cycle would be created: <path>'.","triggerScenarios":"Calling AddDependencies with at least one non-scheduling pair when depRepo.CycleThroughEdges(ctx, pairs) returns a storage/query error.","commonSituations":"Database errors during multi-edge graph traversal; context cancellation on large batches; locked or unavailable Dolt backend.","solutions":["Inspect the wrapped cause after 'final cycle check:'","Retry with a healthy DB and non-canceled context","Split very large batches into smaller requests to reduce traversal cost"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// pre-check: build the graph in memory and topologically verify the batch\n// adds no cycle before calling AddDependencies","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"final cycle check\") {\n    // CycleThroughEdges query failed; inspect wrapped storage cause and retry\n}","preventionTips":["Validate batch acyclicity client-side for large imports","Split huge batches into smaller requests","Keep the Dolt backend healthy and the context alive"],"tags":["dependencies","cycle-detection","database"],"backgroundTag":"cycle-check-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}