gastownhall/beads · error
add dep: dep must not be nil
Error message
add dep: dep must not be nil
What it means
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.
Source
Thrown at internal/storage/domain/dependency.go:273
}
type dependencyUseCaseImpl struct {
depRepo DependencySQLRepository
}
var _ DependencyUseCase = (*dependencyUseCaseImpl)(nil)
func (u *dependencyUseCaseImpl) AddDependency(ctx context.Context, dep *types.Dependency, actor string) error {
return u.add(ctx, dep, actor, false)
}
func (u *dependencyUseCaseImpl) AddWispDependency(ctx context.Context, dep *types.Dependency, actor string) error {
return u.add(ctx, dep, actor, true)
}
func (u *dependencyUseCaseImpl) add(ctx context.Context, dep *types.Dependency, actor string, useWisp bool) error {
if dep == nil {
return fmt.Errorf("add dep: dep must not be nil")
}
if dep.IssueID == "" || dep.DependsOnID == "" {
return fmt.Errorf("add dep: IssueID and DependsOnID must be non-empty")
}
// Self-dependency guard mirrors issueops.CheckDependencyCycleInTx: it is
// checked BEFORE the cycle probe and for ALL dep types, and emits the
// dedicated self-dep message. A blocking self-edge otherwise trips HasCycle
// and would report the wrong (cycle) error (#4547 F-1).
if dep.IssueID == dep.DependsOnID {
return fmt.Errorf("%w: %s cannot depend on itself", ErrSelfDependency, dep.IssueID)
}
if err := u.depRepo.ValidateBlockingHierarchy(ctx, dep); err != nil {
var hierarchyConflict *DependencyHierarchyConflictError
if errors.As(err, &hierarchyConflict) {
return err
}
return fmt.Errorf("add dep: hierarchy check: %w", err)View on GitHub (pinned to 71377f2769)
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
Example fix
// before
var dep *types.Dependency // never assigned
err := uc.AddDependency(ctx, dep, "alice")
// after
dep := &types.Dependency{IssueID: issueID, DependsOnID: blocksID}
if dep == nil { return errors.New("dependency not initialized") }
err := uc.AddDependency(ctx, dep, "alice") Defensive patterns
Strategy: validation
Validate before calling
func validDependency(dep *types.Dependency) bool {
return dep != nil && dep.IssueID != "" && dep.DependsOnID != ""
}
if !validDependency(dep) { return errors.New("dependency must be non-nil with both IDs") } Type guard
func isValidDep(dep *types.Dependency) bool {
return dep != nil
} Prevention
- Always construct Dependency structs explicitly before use
- Nil-check pointers returned from decoders or lookups
- Add constructor helpers (NewDependency(issueID, dependsOnID)) to avoid nils
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- issue must not be nil
- add dep: IssueID and DependsOnID must be non-empty
- proxy.ForceStopUnverified: at most one options value is allo
- db: DependencySQLRepository.Insert: dep must not be nil
- db: DependencySQLRepository.Insert: IssueID must not be empt
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/d343f95df5023ba6.
Report an issue: GitHub.