gastownhall/beads · error

add dep: IssueID and DependsOnID must be non-empty

Error message

add dep: IssueID and DependsOnID must be non-empty

What it means

A validation error from the dependency use case: the Dependency struct was non-nil but IssueID or DependsOnID was empty. A dependency edge requires both endpoints, so the call is rejected before any repository or cycle checks run.

Source

Thrown at internal/storage/domain/dependency.go:276

	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)
	}

	if types.IsSchedulingEdge(dep.Type) {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Set both IssueID and DependsOnID on the Dependency before calling
  2. Validate both IDs are non-empty in the caller prior to the API call
  3. Fix the data source (CLI args, JSON, env) that yielded an empty ID

Example fix

// before
dep := &types.Dependency{IssueID: issueID} // DependsOnID missing
err := uc.AddWispDependency(ctx, dep, actor)
// after
if issueID == "" || dependsOnID == "" { return fmt.Errorf("both issue IDs required") }
dep := &types.Dependency{IssueID: issueID, DependsOnID: dependsOnID}
err := uc.AddWispDependency(ctx, dep, actor)
Defensive patterns

Strategy: validation

Validate before calling

if dep == nil || dep.IssueID == "" || dep.DependsOnID == "" {
    return fmt.Errorf("dependency requires non-empty IssueID and DependsOnID")
}

Type guard

func isCompleteDependency(dep *types.Dependency) bool {
    return dep != nil && dep.IssueID != "" && dep.DependsOnID != ""
}

Prevention

When it happens

Trigger: Calling AddDependency/AddWispDependency with a &types.Dependency{} where IssueID=="" or DependsOnID=="" — e.g. fields not set after construction, or sourced from empty CLI flags/env/JSON fields.

Common situations: Script or command omitted one of the two issue IDs; parsing a dependency record with a missing field; empty variable interpolation in a shell pipeline feeding bd.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


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