gastownhall/beads · error
recompute is_blocked after close for %s: %w
Error message
recompute is_blocked after close for %s: %w
What it means
This error wraps a failure of RecomputeIsBlockedInTxWithResult, which maintains derived is_blocked state on dependent issues after this issue closes. Since blocked-state is derived data, the library refuses to commit a close with a stale/incorrect recompute and rolls back the transaction. The issue ID and underlying error are included for diagnosis.
Source
Thrown at internal/storage/issueops/close.go:382
return &CloseResult{IsWisp: isWisp, AlreadyClosed: true}, nil
}
return nil, fmt.Errorf("failed to close issue: %s", id)
}
// A closed issue holds no lease (no-op for wisps, which are never leased).
if err := DeleteLeaseInTx(ctx, tx, id); err != nil {
return nil, err
}
if recordEvent {
if err := RecordEventInTable(ctx, tx, eventTable, id, types.EventClosed, actor, reason); err != nil {
return nil, fmt.Errorf("failed to record event: %w", err)
}
}
recompute, err := RecomputeIsBlockedInTxWithResult(ctx, tx, affectedIssues, affectedWisps)
if err != nil {
return nil, fmt.Errorf("recompute is_blocked after close for %s: %w", id, err)
}
// Snapshot only after all derived blocked-state maintenance has completed.
// recordEvent gates the human audit event, never the journal.
if err := RecordEventInTx(ctx, tx, EventClose, id, actor); err != nil {
return nil, err
}
return &CloseResult{IsWisp: isWisp, IssueRowsChanged: !isWisp || recompute.IssueRowsChanged}, nil
}
View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped error (errors.Is/As) — deadlocks/timeouts are the usual cause; retry with a fresh transaction.
- Increase the context timeout when closing issues with large dependency fan-out.
- Close issues in smaller batches to reduce transaction size and lock scope.
- Check schema/version consistency (bd doctor) and server health if failures persist.
Defensive patterns
Strategy: retry
Try / catch
err := store.CloseIssue(ctx, id, actor, reason)
if err != nil && strings.Contains(err.Error(), "recompute is_blocked after close") {
if isDeadlockOrTimeout(err) {
return retryClose(ctx, id, actor, reason, 1) // whole tx rolled back
}
return err
} Prevention
- Give large fan-out closes a generous context timeout.
- Close high-dependency issues in small batches, not one giant transaction.
- Stagger concurrent closes of issues sharing dependents to reduce lock contention.
- Keep schema current; verify with bd doctor if errors persist.
When it happens
Trigger: RecomputeIsBlockedInTxWithResult returns an error while updating is_blocked for affectedIssues/affectedWisps inside the close transaction: large dependency graphs timing out, lock contention on dependent rows, missing columns, or connection failure mid-recompute.
Common situations: Closing a hub issue with hundreds of dependents in one transaction under a tight context deadline; deadlocks with other writers updating the same dependent rows; schema drift on the issues table; slow Dolt server under load.
Related errors
- affected by delete for %s: %w
- recompute is_blocked after add dependency %s -> %s: %w
- ErrTransaction
- open unit of work: %w
- failed to begin transaction: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/bb8e32f24ca6898f.
Report an issue: GitHub.