gastownhall/beads · error

delete: recompute is_blocked: %w

Error message

delete: recompute is_blocked: %w

What it means

This error wraps a failure from issueRepo.RecomputeIsBlocked during deleteMany, the final step of deleting issues/wisps. After the dependency, label, event, and issue rows have already been removed, the use case recomputes the is_blocked flag on issues that were affected by the deletion (their blockers changed). The delete itself has already mutated the database, so a failure here leaves the delete partially applied with stale blocked-state flags.

Source

Thrown at internal/storage/domain/issue_delete.go:215

	if err != nil {
		return result, fmt.Errorf("delete: drop issue rows: %w", err)
	}
	wispsDeleted, err := u.issueRepo.DeleteByIDs(ctx, wispIDs, IssueTableOpts{UseWispsTable: true})
	if err != nil {
		return result, fmt.Errorf("delete: drop wisp rows: %w", err)
	}
	result.DeletedCount = issuesDeleted + wispsDeleted

	if params.UpdateTextReferences && len(connected) > 0 {
		refs, err := u.rewriteTextReferences(ctx, allIDs, connected, connectedIsWisp, actor)
		if err != nil {
			return result, fmt.Errorf("delete: rewrite text references: %w", err)
		}
		result.ReferencesUpdated = refs
	}

	if err := u.issueRepo.RecomputeIsBlocked(ctx, affectedIssues, affectedWisps); err != nil {
		return result, fmt.Errorf("delete: recompute is_blocked: %w", err)
	}

	return result, nil
}

// externalDependents finds the direct dependents of each id in ids that are
// not themselves in ids, across both the issue and wisp dependency tables.
// The result maps deletion-set id -> external dependent ids (unsorted).
func (u *issueUseCaseImpl) externalDependents(ctx context.Context, ids []string) (map[string][]string, error) {
	idSet := make(map[string]bool, len(ids))
	for _, id := range ids {
		idSet[id] = true
	}

	issueRes, err := u.depRepo.ListByIssueIDs(ctx, ids, DepListOpts{Direction: DepDirectionIn})
	if err != nil {
		return nil, fmt.Errorf("delete: list dependents: %w", err)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped cause (%w) to identify the driver-level failure and fix that first (connection, timeout, lock).
  2. Retry the delete path: dependency rows are deleted idempotently by ID, so re-running after a transient failure is safe.
  3. Check for concurrent processes modifying the same issues and serialize deletes against them.
  4. If blocked flags are stale but the delete succeeded, run a manual recompute/consistency pass instead of deleting again.

Example fix

// before
if err := u.issueRepo.RecomputeIsBlocked(ctx, affectedIssues, affectedWisps); err != nil {
    return result, fmt.Errorf("delete: recompute is_blocked: %w", err)
}
// after
if err := u.issueRepo.RecomputeIsBlocked(ctx, affectedIssues, affectedWisps); err != nil {
    return result, fmt.Errorf("delete: recompute is_blocked (deleted=%d, affectedIssues=%d): %w", result.DeletedCount, len(affectedIssues), err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go has no pre-call check; verify DB reachability and schema first:
if err := store.PingContext(ctx); err != nil {
    return fmt.Errorf("database unavailable before delete: %w", err)
}

Type guard

// Narrow the wrapped cause with errors.As:
var dbErr *dberrors.DBError
if errors.As(err, &dbErr) {
    // handle driver-level failure specifically
}

Try / catch

res, err := uc.DeleteIssues(ctx, ids, opts)
if err != nil {
    if strings.Contains(err.Error(), "delete: recompute is_blocked") {
        // delete partially applied; inspect wrapped cause, retry or run recompute
        return fmt.Errorf("delete applied but blocked-state recompute failed: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling DeleteIssue, DeleteWisp, DeleteIssues, or DeleteWisps when the underlying RecomputeIsBlocked query fails — e.g. DB connection dropped mid-transaction, lock contention on affected rows, or a storage-driver error scanning affected issues/wisps.

Common situations: Deleting a large batch whose dependency graph touches many issues, causing long-running recompute queries that hit statement timeouts; concurrent writers holding locks on dependent issues; embedded Dolt/driver connection failures during the final phase of a delete.

Related errors


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