gastownhall/beads · error

add deps: classify sources: %w

Error message

add deps: classify sources: %w

What it means

AddDependencies classifies the batch's source issue IDs into regular issues vs wisps (ephemeral issues) with a single batched repository query (WispSourceIDs) made before any write. This error wraps a failure of that lookup, so the root cause is a storage/repository error (query failure, DB unavailable, context canceled), not a validation problem.

Source

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

		// types before the hierarchy/cycle probe, so a scheduling self-edge is
		// typed as ErrSelfDependency instead of tripping HasCycle (or the final
		// CycleThroughEdges gate) and surfacing as a cycle. The message is
		// byte-identical to every other self-dep site so the proxied bulk CLI
		// (bd dep add / bd link) shows one consistent self-dependency error.
		if dep.IssueID == dep.DependsOnID {
			return BulkAddDepsResult{}, fmt.Errorf("%w: %s cannot depend on itself", ErrSelfDependency, dep.IssueID)
		}
	}
	sources := make([]string, 0, len(deps))
	for _, dep := range deps {
		sources = append(sources, dep.IssueID)
	}
	// One query for the batch, read before the first write. Nothing an edge
	// write does moves a source between planes, so the answer stays true for
	// the rest of the unit of work.
	wispSources, err := u.depRepo.WispSourceIDs(ctx, sources)
	if err != nil {
		return BulkAddDepsResult{}, fmt.Errorf("add deps: classify sources: %w", err)
	}
	// Parent-child edges must be visible before blocking edges in the same
	// request. The shared repository guard can then evaluate existing + planned
	// ancestry without widening #4034 into #4035's combined-graph cycle check.
	for phase := 0; phase < 2; phase++ {
		parentPhase := phase == 0
		for i, dep := range deps {
			if (dep.Type == types.DepParentChild) != parentPhase {
				continue
			}
			if err := u.depRepo.ValidateBlockingHierarchy(ctx, dep); err != nil {
				var hierarchyConflict *DependencyHierarchyConflictError
				if errors.As(err, &hierarchyConflict) {
					return BulkAddDepsResult{}, err
				}
				return BulkAddDepsResult{}, fmt.Errorf("add deps[%d]: hierarchy check: %w", i, err)
			}
			if !opts.SkipPerEdgeCycleCheck && types.IsSchedulingEdge(dep.Type) {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped root cause (%w) — usually a Dolt/driver error printed after this prefix
  2. Verify the .beads database is accessible and not locked by another process (bd doctor)
  3. Retry the operation with a fresh, non-expired context
Defensive patterns

Strategy: try-catch

Validate before calling

if err := ctx.Err(); err != nil { return err } // ensure live context before the call

Try / catch

if _, err := uc.AddDependencies(ctx, deps, opts); err != nil {
    if strings.Contains(err.Error(), "classify sources") {
        // storage-layer failure; check DB health and retry
    }
    return err
}

Prevention

When it happens

Trigger: Calling AddDependencies when u.depRepo.WispSourceIDs(ctx, sources) returns an error — e.g. the Dolt database is unreachable, the connection was closed, or ctx was canceled mid-query.

Common situations: Running bd against a locked or corrupt .beads Dolt database; concurrent process holding a conflicting transaction; context timeout expiring during a large batch on slow storage.

Related errors


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