gastownhall/beads · error
db: Update %s: recompute is_blocked: %w
Error message
db: Update %s: recompute is_blocked: %w
What it means
This error wraps a failure of RecomputeIsBlockedInTx, which recalculates is_blocked on all issues affected by a status change during Update. It runs inside the same transaction so derived blocking state stays consistent; if the recompute SQL fails, the Update is aborted with this wrapped error.
Source
Thrown at internal/storage/domain/db/issue.go:324
if statusChanging {
newStatus := coerceStatus(updates["status"])
oldActive := oldIssue.Status != types.StatusClosed && oldIssue.Status != types.StatusPinned
newActive := newStatus != types.StatusClosed && newStatus != types.StatusPinned
if oldActive != newActive {
var (
affectedIssues, affectedWisps []string
aerr error
)
if opts.UseWispsTable {
affectedIssues, affectedWisps, aerr = issueops.AffectedByStatusChangeForWispInTx(ctx, r.runner, id)
} else {
affectedIssues, affectedWisps, aerr = issueops.AffectedByStatusChangeInTx(ctx, r.runner, id)
}
if aerr != nil {
return fmt.Errorf("db: Update %s: affected by status change: %w", id, aerr)
}
if err := issueops.RecomputeIsBlockedInTx(ctx, r.runner, affectedIssues, affectedWisps); err != nil {
return fmt.Errorf("db: Update %s: recompute is_blocked: %w", id, err)
}
}
}
// Snapshot only after all derived blocked-state maintenance has completed.
// The no-op early returns above wrote nothing and journal nothing.
return issueops.RecordEventInTx(ctx, r.runner, issueops.EventUpdate, id, actor)
}
// CompareAndSetMetadataKey runs the SHARED compare-and-set body, unwrapped.
//
// It is the whole of this leg's implementation, and that is the point: the two
// store backends wrap the same function in their own transaction, so the third
// leg is a wrapper check rather than an independent vote — which is what the
// conformance contract's header says.
//
// It does NOT wrap the error the way its siblings above do, for the reason
// WalkDependencyTree gives: the body publishes storage.ErrNotFound and
// storage.ErrValidation as the role's own vocabulary, both classified byView on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped cause (%w) for the underlying driver error (deadlock, timeout, cancellation).
- Increase the operation's context timeout if the recompute over many dependents timed out.
- Retry the update; the transactional rollback leaves state consistent.
- Verify dependency rows and schema integrity for affected issues.
Example fix
// before
ctx := context.Background()
store.Update(ctx, id, opts) // may die mid-recompute on deadline
// after
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := store.Update(ctx, id, opts); err != nil { /* inspect %w cause */ } Defensive patterns
Strategy: retry
Validate before calling
if err := ctx.Err(); err != nil { return fmt.Errorf("context already done: %w", err) } Try / catch
err := store.Update(ctx, id, opts)
if err != nil && strings.Contains(err.Error(), "recompute is_blocked") {
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
err = store.Update(ctx, id, opts)
} Prevention
- Set timeouts proportional to the issue's dependent count.
- Batch status changes during low-contention windows.
- Run bd doctor periodically to catch schema drift early.
- Avoid concurrent bulk updates touching the same dependency graph.
When it happens
Trigger: Calling Update with a status change where RecomputeIsBlockedInTx errors: SQL failure, deadlocked or killed transaction, connection loss, or context cancellation mid-recompute.
Common situations: Long recomputes over many dependents hitting context deadlines; concurrent updates contending on dependent rows; driver/connection errors during large batch updates; wisp vs issue table mismatches.
Related errors
- mark is_blocked after add dependency %s -> %s: %w
- ErrTransaction
- open unit of work: %w
- failed to begin transaction: %w
- failed to commit is_blocked repairs: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/46c6af74ea53807a.
Report an issue: GitHub.