gastownhall/beads · error
recompute is_blocked after status change for %s: %w
Error message
recompute is_blocked after status change for %s: %w
What it means
This error wraps a failure while recomputing the is_blocked flag for issues that depend on an issue whose status just changed. After a status update inside a transaction, issueops recalculates blocked state for all affected issues/wisps via RecomputeIsBlockedInTxWithResult; if that recompute query fails, the whole update transaction is rolled back and this wrapped error is returned.
Source
Thrown at internal/storage/issueops/update.go:533
case types.Status:
newStatus = string(v)
}
oldActive := oldIssue.Status != types.StatusClosed && oldIssue.Status != types.StatusPinned
newActive := newStatus != string(types.StatusClosed) && newStatus != string(types.StatusPinned)
if oldActive != newActive {
var affectedIssues, affectedWisps []string
var aerr error
if isWisp {
affectedIssues, affectedWisps, aerr = AffectedByStatusChangeForWispInTx(ctx, tx, id)
} else {
affectedIssues, affectedWisps, aerr = AffectedByStatusChangeInTx(ctx, tx, id)
}
if aerr != nil {
return nil, fmt.Errorf("affected by status change for %s: %w", id, aerr)
}
recompute, err := RecomputeIsBlockedInTxWithResult(ctx, tx, affectedIssues, affectedWisps)
if err != nil {
return nil, fmt.Errorf("recompute is_blocked after status change for %s: %w", id, err)
}
updateResult.IssueRowsChanged = !isWisp || recompute.IssueRowsChanged
updateResult.WispRowsChanged = isWisp || recompute.WispRowsChanged
}
}
// Snapshot only after all derived blocked-state maintenance has completed,
// so the journal row carries the settled bead. recordEvent controls the
// human-facing audit event only: the journal is a machine replay feed and
// must never have a hole punched in it by an audit-suppressing caller.
if err := RecordEventInTx(ctx, tx, EventUpdate, id, actor); err != nil {
return nil, err
}
return updateResult, nil
}
func cloneUpdateFields(updates map[string]interface{}) map[string]interface{} {
cloned := make(map[string]interface{}, len(updates))View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped %w cause to identify the underlying SQL/DB error and fix that first
- Retry the update transaction; the recompute runs inside the same tx so a transient DB error is safe to retry
- Verify dependency rows (blocked-by relations) are consistent, e.g. via a data-integrity check or re-sync
- If reproducible, capture the failing query from RecomputeIsBlockedInTxWithResult and check schema/version compatibility
Example fix
// before bd update bd-42 --status closed // fails: recompute is_blocked after status change // after // 1. check the wrapped cause bd doctor # or inspect storage logs for the underlying SQL error // 2. repair dangling dependency references, then retry the status change
Defensive patterns
Strategy: retry
Validate before calling
// pre-flight: verify dependency rows are consistent before a status change
rows, err := db.Query("SELECT issue_id FROM dependencies WHERE depends_on_id = ?", issueID)
if err != nil {
return fmt.Errorf("cannot read dependencies, update would fail recompute: %w", err)
} Try / catch
// wrap in retry with backoff for transient DB errors
for i := 0; i < 3; i++ {
_, err := issueops.UpdateIssueInTx(ctx, tx, id, updates)
if err == nil || !strings.Contains(err.Error(), "recompute is_blocked") {
break
}
time.Sleep(backoff(i))
} Prevention
- Keep DB connectivity healthy; recompute runs in the same tx as the update
- Periodically validate dependency edges for dangling references
- Treat any wrapped cause in this error as the real bug and fix it, not the wrapper
When it happens
Trigger: Calling UpdateIssueInTx or UpdateIssueWithoutEventInTx with a status-changing update (e.g. closing an issue) while its dependency graph rows are in a state the recompute SQL cannot handle, or when the underlying database errors during the recompute statement.
Common situations: Database connectivity drops mid-transaction, dependency rows reference issues in unexpected states, schema/lock contention during bulk status changes, or corrupted dependency edges left from a prior failed migration.
Related errors
- failed to remove relates-to %s -> %s: %w
- failed to get dependencies for %s: %w
- add deps: classify sources: %w
- delete: drop deps: %w
- delete: drop wisp deps: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/94911f4d970bd430.
Report an issue: GitHub.