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

  1. Construct and pass a valid &types.Dependency{} with IssueID and DependsOnID set
  2. Nil-check the dependency value before calling AddDependency/AddWispDependency
  3. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/d343f95df5023ba6. Report an issue: GitHub.