{"record":{"id":"ba7c53ae097ce108","repo":"gastownhall/beads","slug":"w-add-dependencies-requires-an-actor","errorCode":null,"errorMessage":"%w: add dependencies requires an actor","messagePattern":"%w: add dependencies requires an actor","errorType":"validation","errorClass":"storage.ErrValidation","httpStatus":null,"severity":"warning","filePath":"internal/storage/issueops/dependency_editor.go","lineNumber":30,"sourceCode":")\n\n// ValidateAddDependenciesRequest applies the request rules every\n// DependencyEditor implementation shares. It lives here rather than in each of\n// them because a rule enforced on one backend and not the other is not a\n// contract.\n//\n// The self-dependency refusal is here, ahead of the per-edge cycle probe and\n// for EVERY edge type, for the reason the domain path states: a blocking\n// self-edge otherwise trips the cycle check and reports the wrong refusal, and\n// SkipPerEdgeCycleCheck would skip it entirely.\n//\n// The type check is that there IS a type — non-empty, within the column's\n// length. It is deliberately not a membership test: the vocabulary is an open,\n// 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}","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependency_editor.go#L12-L48","documentation":"ValidateAddDependenciesRequest enforces that every dependency-creation request names the actor performing the change, because beads records who added each dependency for audit and permission purposes. When request.Actor is empty the request is rejected, wrapped with storage.ErrValidation so callers can errors.Is() it. This is a pure validation error raised before any database work.","triggerScenarios":"Calling AddDependencies (or building a publicops.AddDependenciesRequest) with Actor left as the zero value \"\" — e.g. CLI/agent code that didn't capture the current user identity.","commonSituations":"Automated scripts constructing requests manually without setting Actor; refactors that removed the actor plumbing; sessions with no configured user identity.","solutions":["Set request.Actor to the active identity (configured user, agent name, or authenticated principal).","Configure the identity source (e.g. bd config / env) so callers always have an actor available.","Reject empty actors at your own boundary before invoking the storage layer.","Check errors.Is(err, storage.ErrValidation) to confirm it's this validation, not a storage failure."],"exampleFix":"// before\nreq := publicops.AddDependenciesRequest{Edges: edges}\n// after\nreq := publicops.AddDependenciesRequest{Actor: \"agent:claude\", Edges: edges}","handlingStrategy":"validation","validationCode":"if req.Actor == \"\" {\n    return fmt.Errorf(\"actor must be set (e.g. \\\"agent:claude\\\") before calling AddDependencies\")\n}","typeGuard":"func hasActor(req publicops.AddDependenciesRequest) bool { return req.Actor != \"\" }","tryCatchPattern":"err := store.AddDependencies(ctx, req)\nif errors.Is(err, storage.ErrValidation) {\n    // re-prompt for identity or fix request construction\n}","preventionTips":["Resolve the actor from a single identity helper so it's never empty.","Construct requests through a constructor that requires an actor parameter.","Always set Actor in automated/agent scripts."],"tags":["validation","dependencies","api-misuse"],"backgroundTag":"missing-actor","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}