gastownhall/beads · error

db: DependencySQLRepository.Delete: affected set: %w

Error message

db: DependencySQLRepository.Delete: affected set: %w

What it means

Wraps a failure from computing the set of issues/wisps affected by a dependency removal (AffectedByDepChangeInTx or AffectedByDepChangeForWispInTx). After the edge is deleted, the repository needs the transitive affected set to refresh is_blocked; if that query fails the delete is aborted with this error so blocked-state never goes stale.

Source

Thrown at internal/storage/domain/db/dependency.go:381

			IssueID:  issueID,
			Type:     types.EventDependencyRemoved,
			Actor:    actor,
			NewValue: fmt.Sprintf("Removed dependency on %s", dependsOnID),
		}, domain.RecordEventOpts{UseWispsTable: opts.UseWispsTable}); err != nil {
			return domain.DepDeleteResult{}, fmt.Errorf("db: DependencySQLRepository.Delete: record dependency_removed event: %w", err)
		}
	}

	dt := types.DependencyType(depType)
	var affectedIssues, affectedWisps []string
	var aerr error
	if opts.UseWispsTable {
		affectedIssues, affectedWisps, aerr = issueops.AffectedByDepChangeForWispInTx(ctx, r.runner, issueID, dependsOnID, dt)
	} else {
		affectedIssues, affectedWisps, aerr = issueops.AffectedByDepChangeInTx(ctx, r.runner, issueID, dependsOnID, dt)
	}
	if aerr != nil {
		return domain.DepDeleteResult{}, fmt.Errorf("db: DependencySQLRepository.Delete: affected set: %w", aerr)
	}
	if err := issueops.RecomputeIsBlockedInTx(ctx, r.runner, affectedIssues, affectedWisps); err != nil {
		return domain.DepDeleteResult{}, fmt.Errorf("db: DependencySQLRepository.Delete: recompute is_blocked: %w", err)
	}

	// Snapshot only after all derived blocked-state maintenance has completed.
	// Never gated on opts.EmitEvent — a structural removal is as real to a
	// replaying consumer as one from an explicit dep verb.
	if err := issueops.RecordDepEventInTx(ctx, r.runner, issueops.EventDepRemove, issueID, depType, dependsOnID, depMetadata, actor); err != nil {
		return domain.DepDeleteResult{}, err
	}

	return domain.DepDeleteResult{Found: true, Type: dt, DependsOnID: dependsOnID}, nil
}

func (r *dependencySQLRepositoryImpl) HasCycle(ctx context.Context, issueID, dependsOnID string) (bool, error) {
	if issueID == "" || dependsOnID == "" {
		return false, errors.New("db: DependencySQLRepository.HasCycle: issueID and dependsOnID must not be empty")

View on GitHub (pinned to 71377f2769)

Solutions

  1. Re-run the Delete with a longer context timeout / larger deadline; it is idempotent (second run returns Found:false but still recomputes nothing needed).
  2. Check ctx cancellation: ensure the caller's context isn't expiring during the transitive query.
  3. Verify opts.UseWispsTable matches where the dependency was stored.
  4. Inspect DB health (locks, connection pool exhaustion) if failures cluster under load.

Example fix

// before
ctx := context.Background() // unbounded or too-short deadline elsewhere
res, err := deps.Delete(ctx, id, depID, actor, opts)
// after
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
res, err := deps.Delete(ctx, id, depID, actor, opts)
Defensive patterns

Strategy: retry

Validate before calling

if err := ctx.Err(); err != nil { return err }
if issueID == "" || dependsOnID == "" { return errors.New("ids required") }

Try / catch

if err != nil && strings.Contains(err.Error(), "affected set") {
    // retry with longer deadline; Delete is idempotent (Found:false if already deleted)
    ctx2, cancel := context.WithTimeout(context.Background(), 60*time.Second)
    defer cancel()
    _, err = deps.Delete(ctx2, issueID, depID, actor, opts)
}

Prevention

When it happens

Trigger: DependencySQLRepository.Delete succeeds in deleting the edge, then the recursive/transitive affected-set query fails: connection loss, query timeout on large dependency graphs, SQL syntax/schema mismatch, or context cancellation mid-query.

Common situations: Very large dependency graphs timing out; context deadline exceeded because caller set a short timeout; Dolt server restarted mid-transaction; wrong UseWispsTable flag selecting an empty/absent table.

Related errors


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