{"record":{"id":"d343f95df5023ba6","repo":"gastownhall/beads","slug":"add-dep-dep-must-not-be-nil","errorCode":null,"errorMessage":"add dep: dep must not be nil","messagePattern":"add dep: dep must not be nil","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/dependency.go","lineNumber":273,"sourceCode":"}\n\ntype dependencyUseCaseImpl struct {\n\tdepRepo DependencySQLRepository\n}\n\nvar _ DependencyUseCase = (*dependencyUseCaseImpl)(nil)\n\nfunc (u *dependencyUseCaseImpl) AddDependency(ctx context.Context, dep *types.Dependency, actor string) error {\n\treturn u.add(ctx, dep, actor, false)\n}\n\nfunc (u *dependencyUseCaseImpl) AddWispDependency(ctx context.Context, dep *types.Dependency, actor string) error {\n\treturn u.add(ctx, dep, actor, true)\n}\n\nfunc (u *dependencyUseCaseImpl) add(ctx context.Context, dep *types.Dependency, actor string, useWisp bool) error {\n\tif dep == nil {\n\t\treturn fmt.Errorf(\"add dep: dep must not be nil\")\n\t}\n\tif dep.IssueID == \"\" || dep.DependsOnID == \"\" {\n\t\treturn fmt.Errorf(\"add dep: IssueID and DependsOnID must be non-empty\")\n\t}\n\n\t// Self-dependency guard mirrors issueops.CheckDependencyCycleInTx: it is\n\t// checked BEFORE the cycle probe and for ALL dep types, and emits the\n\t// dedicated self-dep message. A blocking self-edge otherwise trips HasCycle\n\t// and would report the wrong (cycle) error (#4547 F-1).\n\tif dep.IssueID == dep.DependsOnID {\n\t\treturn fmt.Errorf(\"%w: %s cannot depend on itself\", ErrSelfDependency, dep.IssueID)\n\t}\n\tif err := u.depRepo.ValidateBlockingHierarchy(ctx, dep); err != nil {\n\t\tvar hierarchyConflict *DependencyHierarchyConflictError\n\t\tif errors.As(err, &hierarchyConflict) {\n\t\t\treturn err\n\t\t}\n\t\treturn fmt.Errorf(\"add dep: hierarchy check: %w\", err)","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/dependency.go#L255-L291","documentation":"A guard-clause error from the dependency use case: the caller passed a nil *types.Dependency to add(), which backs both AddDependency and AddWispDependency. The library cannot create a dependency edge without a populated struct, so it rejects the call immediately before any database work.","triggerScenarios":"Calling AddDependency(ctx, nil, actor) or AddWispDependency(ctx, nil, actor), or passing the result of a failed/empty lookup (nil pointer) without checking it first.","commonSituations":"A JSON/CLI decode produced a nil Dependency; a variable populated conditionally was nil on a code path; refactoring changed a return type from value to pointer without nil checks.","solutions":["Construct and pass a valid &types.Dependency{} with IssueID and DependsOnID set","Nil-check the dependency value before calling AddDependency/AddWispDependency","Trace where the nil came from (failed unmarshal or lookup) and handle that upstream"],"exampleFix":"// before\nvar dep *types.Dependency // never assigned\nerr := uc.AddDependency(ctx, dep, \"alice\")\n// after\ndep := &types.Dependency{IssueID: issueID, DependsOnID: blocksID}\nif dep == nil { return errors.New(\"dependency not initialized\") }\nerr := uc.AddDependency(ctx, dep, \"alice\")","handlingStrategy":"validation","validationCode":"func validDependency(dep *types.Dependency) bool {\n    return dep != nil && dep.IssueID != \"\" && dep.DependsOnID != \"\"\n}\nif !validDependency(dep) { return errors.New(\"dependency must be non-nil with both IDs\") }","typeGuard":"func isValidDep(dep *types.Dependency) bool {\n    return dep != nil\n}","tryCatchPattern":null,"preventionTips":["Always construct Dependency structs explicitly before use","Nil-check pointers returned from decoders or lookups","Add constructor helpers (NewDependency(issueID, dependsOnID)) to avoid nils"],"tags":["validation","nil-pointer","dependency","api-misuse"],"backgroundTag":"nil-argument-validation","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}