gastownhall/beads · error

recompute is_blocked after batch delete: %w

Error message

recompute is_blocked after batch delete: %w

What it means

After rows are deleted and journaled, DeleteResolvedSetInTx recomputes the is_blocked flag on issues that depended on the deleted ones. Failure of RecomputeIsBlockedInTx is wrapped with this message. The rows are already deleted in this transaction, so a failure here rolls everything back.

Source

Thrown at internal/storage/issueops/delete.go:346

			fmt.Sprintf(`DELETE FROM leases WHERE issue_id IN (%s)`, batchInClause),
			batchArgs...); err != nil {
			return nil, fmt.Errorf("delete leases: %w", err)
		}
	}
	result.DeletedCount = totalRegularsDeleted + len(set.WispIDs)

	// Journal every regular issue this bulk/cascade delete removed. Wisps went
	// through deleteIssueRowInTx above, which journals each itself; set.All is
	// cascade-expanded, so this records cascade deletes too. The delete
	// plumbing carries no actor, so the rows record none.
	for _, id := range journaledDeletes {
		if err := RecordDeleteInTx(ctx, tx, id, ""); err != nil {
			return nil, err
		}
	}

	if err := RecomputeIsBlockedInTx(ctx, tx, affectedIssues, affectedWisps); err != nil {
		return nil, fmt.Errorf("recompute is_blocked after batch delete: %w", err)
	}

	return result, nil
}

// ExistingIssueIDsInTableInTx returns the requested IDs that currently exist
// in the selected issue table. It preserves caller ordering so delete and
// journal records are deterministic across batches.
func ExistingIssueIDsInTableInTx(ctx context.Context, tx DBTX, table string, ids []string) ([]string, error) {
	if len(ids) == 0 {
		return nil, nil
	}
	switch table {
	case "issues", "wisps":
	default:
		return nil, fmt.Errorf("unsupported issue table %q", table)
	}
	exists := make(map[string]struct{}, len(ids))

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped inner error from RecomputeIsBlockedInTx for the root cause
  2. Increase the context timeout for large cascades
  3. Repair dependency-table consistency (bd doctor / integrity check) if the recompute queries fail on bad rows
  4. Retry the delete; the transaction is atomic so no partial state remains

Example fix

// before
deleteCtx := ctx
// after
deleteCtx, cancel := context.WithTimeout(ctx, 2*time.Minute)
defer cancel()
// pass deleteCtx so RecomputeIsBlockedInTx is not cut short
Defensive patterns

Strategy: retry

Validate before calling

// sanity-check dependency table integrity before a big cascade
rows, _ := db.Query("SELECT COUNT(*) FROM dependencies WHERE issue_id IS NULL OR depends_on_id IS NULL")
// nonzero NULL count => repair before deleting

Try / catch

res, err := DeleteIssuesInTx(ctx, tx, ids, opts)
if err != nil && strings.Contains(err.Error(), "recompute is_blocked") {
	// whole tx rolled back; retry with longer deadline after repair
	return retryWithTimeout(ctx, ids, 2*time.Minute)
}
return err

Prevention

When it happens

Trigger: DeleteIssuesInTx/DeleteInTx where the post-delete dependency recompute fails — corrupt or missing dependency rows, query error on dependencies/wisp_dependencies tables, or context cancellation during the recompute.

Common situations: Very large cascade deletes making the recompute slow enough to hit a context deadline; inconsistent dependency tables after a failed earlier sync.

Related errors


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