gastownhall/beads · error
db: DependencySQLRepository.Insert: affected set: %w
Error message
db: DependencySQLRepository.Insert: affected set: %w
What it means
This error wraps a failure from computing the set of issues affected by a dependency change (AffectedByDepChangeInTx / AffectedByDepChangeForWispInTx), which expands the source by parent-child descendants and waiters. This set drives is_blocked propagation, so the insert aborts before marking.
Source
Thrown at internal/storage/domain/db/dependency.go:206
// is_blocked maintenance mirrors the classic AddDependencyInTx flow
// (issueops/dependencies.go): the affected set expands the source by its
// parent-child descendants (plus, for parent-child edges, waiters on the
// target spawner), then a Mark pass propagates blocked state — or, for
// parent-child adds (not monotonic: an already-closed child can satisfy an
// any-children waits-for gate), a full mark/unmark Recompute. Skipping the
// expansion left descendants stale when a blocking edge landed on their
// ancestor (bd-6dnrw.44 item 3).
srcIsWisp := opts.UseWispsTable
var affectedIssues, affectedWisps []string
var aerr error
if srcIsWisp {
affectedIssues, affectedWisps, aerr = issueops.AffectedByDepChangeForWispInTx(ctx, r.runner, dep.IssueID, dep.DependsOnID, dep.Type)
} else {
affectedIssues, affectedWisps, aerr = issueops.AffectedByDepChangeInTx(ctx, r.runner, dep.IssueID, dep.DependsOnID, dep.Type)
}
if aerr != nil {
return fmt.Errorf("db: DependencySQLRepository.Insert: affected set: %w", aerr)
}
if dep.Type == types.DepBlocks || dep.Type == types.DepConditionalBlocks {
if err := r.markDirectBlockedSource(ctx, dep.IssueID, srcIsWisp, dep.DependsOnID, targetCol); err != nil {
return fmt.Errorf("db: DependencySQLRepository.Insert: mark is_blocked: %w", err)
}
affectedIssues, affectedWisps = issueops.RemoveSourceFromAffected(dep.IssueID, srcIsWisp, affectedIssues, affectedWisps)
}
if dep.Type == types.DepParentChild {
if err := issueops.RecomputeIsBlockedInTx(ctx, r.runner, affectedIssues, affectedWisps); err != nil {
return fmt.Errorf("db: DependencySQLRepository.Insert: recompute is_blocked: %w", err)
}
// Snapshot only after all derived blocked-state maintenance has completed.
return issueops.RecordDepEventInTx(ctx, r.runner, issueops.EventDepAdd, dep.IssueID, string(dep.Type), dep.DependsOnID, metadata, actor)
}
if err := issueops.MarkIsBlockedInTx(ctx, r.runner, affectedIssues, affectedWisps); err != nil {
return fmt.Errorf("db: DependencySQLRepository.Insert: mark is_blocked (affected): %w", err)
}
// Snapshot only after all derived blocked-state maintenance has completed.View on GitHub (pinned to 71377f2769)
Solutions
- Retry after confirming DB connectivity (transient failure).
- Verify schema matches current beads version (bd doctor / migrate).
- For very large hierarchies, check for query timeouts in the driver config.
- Inspect the wrapped aerr for the failing query.
Defensive patterns
Strategy: retry
Try / catch
if err := repo.Insert(ctx, dep, actor, opts); err != nil {
if strings.Contains(err.Error(), "affected set") {
// transient traversal failure: backoff and retry, then run blocked-state repair
}
} Prevention
- Keep parent-child hierarchies shallow where possible.
- Run bd doctor to detect schema drift.
- Retry transient DB failures with backoff.
When it happens
Trigger: Calling Insert where the affected-set query fails — recursive/descendant expansion query hits a driver error (connection loss, missing column, timeout on very deep hierarchies).
Common situations: Schema drift removing columns the affected-set query expects; timeouts on extremely deep parent-child trees; transient DB connection failures.
Related errors
- db: Update %s: recompute is_blocked: %w
- mark is_blocked after add dependency %s -> %s: %w
- ErrTransaction
- ErrQuery
- ErrScan
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/2468edb9d11d7e46.
Report an issue: GitHub.