{"record":{"id":"0d46ec0df801ccdd","repo":"gastownhall/beads","slug":"w-s-cannot-depend-on-itself-0d46ec","errorCode":null,"errorMessage":"%w: %s cannot depend on itself","messagePattern":"%w: (.+?) cannot depend on itself","errorType":"validation","errorClass":"domain.ErrSelfDependency","httpStatus":null,"severity":"warning","filePath":"internal/storage/issueops/dependency_editor.go","lineNumber":44,"sourceCode":"// workspace-configurable set (see the Dep* constants), so refusing an unlisted\n// type would refuse a workspace's own.\nfunc ValidateAddDependenciesRequest(request publicops.AddDependenciesRequest) error {\n\tif request.Actor == \"\" {\n\t\treturn fmt.Errorf(\"%w: add dependencies requires an actor\", storage.ErrValidation)\n\t}\n\tif len(request.Edges) == 0 {\n\t\treturn fmt.Errorf(\"%w: add dependencies requires at least one edge\", storage.ErrValidation)\n\t}\n\tfor i, edge := range request.Edges {\n\t\tif edge.IssueID == \"\" || edge.DependsOnID == \"\" {\n\t\t\treturn fmt.Errorf(\"%w: add dependencies edge %d requires both endpoints\", storage.ErrValidation, i)\n\t\t}\n\t\tif !edge.Type.IsValid() {\n\t\t\treturn fmt.Errorf(\"%w: add dependencies edge %d requires a dependency type (max %d chars)\",\n\t\t\t\tstorage.ErrValidation, i, types.MaxDependencyTypeLen)\n\t\t}\n\t\tif edge.IssueID == edge.DependsOnID {\n\t\t\treturn fmt.Errorf(\"%w: %s cannot depend on itself\", domain.ErrSelfDependency, edge.IssueID)\n\t\t}\n\t}\n\treturn nil\n}\n\n// ValidateRemoveDependencyRequest applies the request rules every\n// DependencyEditor implementation shares for a removal.\nfunc ValidateRemoveDependencyRequest(request publicops.RemoveDependencyRequest) error {\n\tif request.Actor == \"\" {\n\t\treturn fmt.Errorf(\"%w: remove dependency requires an actor\", storage.ErrValidation)\n\t}\n\tif request.IssueID == \"\" || request.DependsOnID == \"\" {\n\t\treturn fmt.Errorf(\"%w: remove dependency requires both endpoints\", storage.ErrValidation)\n\t}\n\treturn nil\n}\n\n// AddDependenciesCommitMessage is the history entry an edge assertion records.","sourceCodeStart":26,"sourceCodeEnd":62,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependency_editor.go#L26-L62","documentation":"Self-dependencies (an issue depending on itself) are rejected because they're meaningless and would pollute the dependency graph. The validator compares IssueID and DependsOnID per edge and fails with domain.ErrSelfDependency (wrapped) plus the offending issue ID. This is distinct from storage.ErrValidation so callers can distinguish graph-rule violations from generic request validation.","triggerScenarios":"Calling AddDependencies with an edge where IssueID == DependsOnID — e.g. generated edges from data where both endpoints resolved to the same issue, or CLI arguments where the user passed the same ID twice.","commonSituations":"Bulk imports where a normalized/uppercased ID matched itself; scripts computing edges from text mentions that included the source issue; user typos entering the same bd-xxx twice.","solutions":["Filter edges where IssueID == DependsOnID before building the request.","Match errors.Is(err, domain.ErrSelfDependency) to detect this case programmatically.","Fix the upstream edge-generation logic so source issues are excluded from their own dependency lists.","Verify ID normalization (case/whitespace) isn't accidentally making two IDs equal."],"exampleFix":"// before\nedges := buildEdges(issueID, mentionedIDs) // may include issueID itself\n// after\nedges := buildEdges(issueID, mentionedIDs)\nedges = slices.DeleteFunc(edges, func(e publicops.DependencyEdge) bool {\n    return e.IssueID == e.DependsOnID\n})","handlingStrategy":"validation","validationCode":"req.Edges = slices.DeleteFunc(req.Edges, func(e publicops.DependencyEdge) bool {\n    return e.IssueID == e.DependsOnID\n})","typeGuard":"func isSelfDependency(e publicops.DependencyEdge) bool {\n    return e.IssueID == e.DependsOnID\n}","tryCatchPattern":"err := store.AddDependencies(ctx, req)\nif errors.Is(err, domain.ErrSelfDependency) {\n    // skip the self-edge; not a data-integrity problem\n}","preventionTips":["Filter self-edges in any edge-generation/import code.","Normalize IDs (trim/case) before comparing endpoints.","Never pass the same issue ID to both sides of a dependency flag in CLI usage."],"tags":["validation","dependencies","self-dependency","graph-rules"],"backgroundTag":"self-dependency-rejected","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}