gastownhall/beads · error
db: DependencySQLRepository.Delete: recompute is_blocked: %w
Error message
db: DependencySQLRepository.Delete: recompute is_blocked: %w
What it means
Wraps a failure from RecomputeIsBlockedInTx, which refreshes the is_blocked flag for every issue/wisp in the affected set after a dependency deletion. The library aborts the Delete with this error because leaving is_blocked stale would corrupt readiness computation ('ready' work queues) for all consumers.
Source
Thrown at internal/storage/domain/db/dependency.go:384
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")
}
cycle, err := issueops.WouldCreateSchedulingCycleInTx(ctx, r.runner, issueID, dependsOnID, nil)View on GitHub (pinned to 71377f2769)
Solutions
- Retry the Delete; if the edge row is already gone, the call returns Found:false and you can re-run RecomputeIsBlocked for the affected issues manually if needed.
- Serialize concurrent writers on the same issue graph (per-issue locking in the caller) to avoid conflicts.
- Check for missing referenced issues (deleted concurrently) and ensure soft-delete flow rather than hard delete.
- Verify DB transaction/lock health; reduce batch size by splitting large affected sets if the driver errors on huge statements.
Example fix
// before
res, err := deps.Delete(ctx, issueID, depID, actor, opts)
if err != nil { log.Fatal(err) } // stale is_blocked risk unexamined
// after
res, err := deps.Delete(ctx, issueID, depID, actor, opts)
if err != nil {
if strings.Contains(err.Error(), "recompute is_blocked") {
// retry or manually recompute for affected issues
}
} Defensive patterns
Strategy: retry
Validate before calling
if err := ctx.Err(); err != nil { return err } Try / catch
if err != nil && strings.Contains(err.Error(), "recompute is_blocked") {
// edge may already be deleted; retry is safe (Found:false), or manually
// recompute is_blocked for the affected issues via the recomputation helper
} Prevention
- Serialize writers on the same issue graph to avoid recompute conflicts.
- Prefer soft-delete of issues so affected-set recompute never hits missing rows.
- Retry transient failures — the operation is idempotent.
- Watch Dolt transaction-conflict logs under concurrent write load.
When it happens
Trigger: Delete deletes the edge and computes the affected set fine, but the batch UPDATE recomputing is_blocked fails: DB write error, one of the affected issue IDs no longer exists (FK/row-missing error in strict mode), connection drop, or context cancellation.
Common situations: Concurrent deletion of an issue in the affected set racing the recompute; Dolt transaction conflict under concurrent writers; oversized affected set hitting packet/lock limits; connection pool exhausted.
Related errors
- db: DependencySQLRepository.Delete: record dependency_remove
- db: DependencySQLRepository.Delete: affected set: %w
- db: DependencySQLRepository.HasCycle: %w
- add dep: hierarchy check: %w
- recompute is_blocked after source-repo delete: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/e58f4e73a563d7c7.
Report an issue: GitHub.