{"record":{"id":"a30323b45f050381","repo":"gastownhall/beads","slug":"w-add-dependencies-edge-d-requires-a-dependency","errorCode":null,"errorMessage":"%w: add dependencies edge %d requires a dependency type (max %d chars)","messagePattern":"%w: add dependencies edge (.+?) requires a dependency type \\(max (.+?) chars\\)","errorType":"validation","errorClass":"storage.ErrValidation","httpStatus":null,"severity":"warning","filePath":"internal/storage/issueops/dependency_editor.go","lineNumber":40,"sourceCode":"// 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}\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}","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/dependency_editor.go#L22-L58","documentation":"Each dependency edge must carry a valid dependency type (edge.Type.IsValid()) — non-empty and no longer than types.MaxDependencyTypeLen. Beads deliberately treats the type vocabulary as open (workspace-configurable), so the check is only for presence/length, not membership in a fixed list. Failures are wrapped with storage.ErrValidation.","triggerScenarios":"Calling AddDependencies with an edge whose Type is the zero value (\"\") or exceeds MaxDependencyTypeLen characters — e.g. free-form type strings from user input that weren't truncated.","commonSituations":"Import scripts passing arbitrary dependency labels without validating length; forgetting to default the type when constructing edges manually; schema changes that introduced longer custom types.","solutions":["Set a known type constant (e.g. types.DepBlocks) or a valid workspace-configured type on every edge.","Truncate or reject custom type names longer than types.MaxDependencyTypeLen before calling.","Default empty types to the canonical value your workflow expects.","Match errors.Is(err, storage.ErrValidation) to confirm it's this check."],"exampleFix":"// before\nedge := publicops.DependencyEdge{IssueID: a, DependsOnID: b} // Type empty\n// after\nedge := publicops.DependencyEdge{IssueID: a, DependsOnID: b, Type: types.DepBlocks}","handlingStrategy":"validation","validationCode":"for i, e := range req.Edges {\n    if !e.Type.IsValid() {\n        return fmt.Errorf(\"edge %d has invalid type %q\", i, e.Type)\n    }\n}","typeGuard":"func validEdgeType(t types.DependencyType) bool {\n    return t != \"\" && len(t) <= types.MaxDependencyTypeLen\n}","tryCatchPattern":"err := store.AddDependencies(ctx, req)\nif errors.Is(err, storage.ErrValidation) {\n    // fix the type (set a known constant or truncate) and retry\n}","preventionTips":["Default edge types to a known constant instead of leaving zero value.","Clamp custom type names to types.MaxDependencyTypeLen at input time.","Use types.Dep* constants rather than raw strings."],"tags":["validation","dependencies","dependency-type"],"backgroundTag":"invalid-enum-value","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}