gastownhall/beads · error

add dep: hierarchy check: %w

Error message

add dep: hierarchy check: %w

What it means

This error wraps a failure from depRepo.ValidateBlockingHierarchy during dependency creation. The hierarchy check verifies blocking-dependency constraints; only recognized *DependencyHierarchyConflictError values are passed through as-is — any other repository error (SQL failure, context cancellation, unexpected backend state) is wrapped as 'add dep: hierarchy check:'.

Source

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

		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) {
		cycle, err := u.depRepo.HasCycle(ctx, dep.IssueID, dep.DependsOnID)
		if err != nil {
			return fmt.Errorf("add dep: cycle check: %w", err)
		}
		if cycle {
			// Match the embedded store's user-facing wording verbatim (no ids
			// prefix) so gc code that string-matches this error behaves the same
			// on both plumbings (#4547 F-1).
			return ErrDependencyCycle
		}
	}

	if err := u.depRepo.Insert(ctx, dep, actor, DepInsertOpts{UseWispsTable: useWisp, HierarchyValidated: true, CycleValidated: true, EmitEvent: true}); err != nil {
		// The retype conflict is a user-facing error whose message already
		// matches embedded verbatim; pass it through unwrapped so the CLI does

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped cause after 'hierarchy check:' for the repository/driver error
  2. Retry on transient connection/deadline errors
  3. If the cause is a *DependencyHierarchyConflictError it is returned unwrapped — handle that separately as a domain conflict
  4. Verify hierarchy-related tables are intact (bd doctor / schema check) if errors persist

Example fix

// before
err := uc.AddDependency(ctx, dep, actor)
if err != nil { return err }
// after
err := uc.AddDependency(ctx, dep, actor)
var conflict *storage.DependencyHierarchyConflictError
if errors.As(err, &conflict) { return fmt.Errorf("hierarchy conflict: %s", conflict.Error()) }
if isTransient(err) { retry(...) }
return err
Defensive patterns

Strategy: try-catch

Validate before calling

ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
defer cancel() // hierarchy traversal can be slow; avoid deadline surprises
if err := pingDB(ctx); err != nil { return err }

Type guard

func isHierarchyConflict(err error) bool {
    var hce *storage.DependencyHierarchyConflictError
    return errors.As(err, &hce)
}

Try / catch

err := uc.AddDependency(ctx, dep, actor)
if err != nil {
    var hce *storage.DependencyHierarchyConflictError
    if errors.As(err, &hce) { return err } // domain conflict, surface as-is
    if isTransient(err) { return retryAdd(ctx, dep, actor) }
    return fmt.Errorf("could not add dependency: %w", err)
}

Prevention

When it happens

Trigger: Calling AddDependency/AddWispDependency (with a non-scheduling or any dep reaching the check) where ValidateBlockingHierarchy fails with a non-hierarchy-conflict error: database query failure, connection loss, cancelled context, or an unexpected repository error inside the hierarchy validation.

Common situations: Database unavailable or timing out during hierarchy traversal; corrupted hierarchy data causing the validation query to error; context deadline exceeded while validating deep blocking chains.

Related errors


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