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
- Inspect the wrapped cause (%w) to identify the driver-level failure and fix that first (connection, timeout, lock).
- Retry the delete path: dependency rows are deleted idempotently by ID, so re-running after a transient failure is safe.
- Check for concurrent processes modifying the same issues and serialize deletes against them.
- 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
- Keep delete batches small to limit recompute scope
- Avoid concurrent writers on the same issues during deletes
- Set generous statement timeouts for dependency-heavy deletes
- Always log the fully wrapped error chain with %w/%+v
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
- loading subgraph for %s: %w
- failed to delete child %s: %w
- remove dep %s -> %s: %w
- delete: list dependents: %w
- previewDelete: load issues: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/3779e0172f5176f0.
Report an issue: GitHub.