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

  1. 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.
  2. Serialize concurrent writers on the same issue graph (per-issue locking in the caller) to avoid conflicts.
  3. Check for missing referenced issues (deleted concurrently) and ensure soft-delete flow rather than hard delete.
  4. 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

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


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