{"record":{"id":"952d1170a7057d61","repo":"gastownhall/beads","slug":"w-remove-dependency-requires-both-endpoints","errorCode":null,"errorMessage":"%w: remove dependency requires both endpoints","messagePattern":"%w: remove dependency requires both endpoints","errorType":"validation","errorClass":"storage.ErrValidation","httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/dependency_editor.go","lineNumber":57,"sourceCode":"\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.\n//\n// Unlike CloseBatchCommitMessage it is composed from the REQUEST rather than\n// the result, and that difference is the contract's: the request is\n// all-or-nothing, so what was asked for is exactly what landed or nothing did.\n// A batch close has to name what landed because it can skip an id; this cannot.\n//\n// The two spellings are the two the CLI already wrote — one edge names both\n// endpoints, several name their count — because the role is what both `bd dep\n// add <a> <b>` and `bd dep add --file` now go through, and `bd dolt log` should\n// keep reading the way it did. The count spelling is keyed on the EDGE COUNT\n// and not on which flag was used, so a one-line bulk file now names its edge\n// instead of reading \"add 1 edges\": one edge is one edge however it was\n// spelled.","sourceCodeStart":39,"sourceCodeEnd":75,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependency_editor.go#L39-L75","documentation":"ValidateRemoveDependencyRequest rejects a RemoveDependencyRequest missing either endpoint of the dependency edge: the IssueID whose dependency is being removed, or the DependsOnID it pointed at. An edge removal needs both ends to identify the single row to delete, so requests with either ID empty are refused with storage.ErrValidation before touching storage.","triggerScenarios":"Calling a DependencyEditor's remove-dependency path with publicops.RemoveDependencyRequest where request.IssueID == \"\" or request.DependsOnID == \"\" — typically when one side of the edge comes from optional user input or a variable that failed to resolve.","commonSituations":"A CLI user passing only one issue ID to a dependency-remove command; scripting that interpolates an empty variable for one endpoint; list-driven bulk removal where some entries lack a DependsOnID value.","solutions":["Populate both request.IssueID and request.DependsOnID with the two endpoints of the edge being removed.","If only one ID is known, look up the dependency list for that issue first (e.g. a dependency-records query) to find the missing endpoint.","Add a pre-flight check in the calling layer that surfaces 'both issue IDs are required' to the user instead of reaching storage."],"exampleFix":"// before\nreq := publicops.RemoveDependencyRequest{IssueID: \"bd-42\", Actor: \"alice\"} // DependsOnID empty\nerr := editor.ExecuteRemoveDependency(ctx, req)\n\n// after\nreq := publicops.RemoveDependencyRequest{IssueID: \"bd-42\", DependsOnID: \"bd-7\", Actor: \"alice\"}\nerr := editor.ExecuteRemoveDependency(ctx, req)","handlingStrategy":"validation","validationCode":"if req.IssueID == \"\" || req.DependsOnID == \"\" {\n    return fmt.Errorf(\"remove dependency: both issue and depends-on IDs are required\")\n}\n// proceed with req","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Resolve both endpoints from the dependency list of the issue before issuing a removal.","Validate user-supplied IDs at the CLI/handler boundary before building the request struct.","In bulk removal loops, skip or log entries with missing endpoints rather than passing them through."],"tags":["go","validation","dependencies"],"backgroundTag":"missing-required-field","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}