gastownhall/beads · error
begin is_blocked recompute
Error message
begin is_blocked recompute: %w
What it means
RecomputeAllBlockedOnConn opens a transaction for the is_blocked recompute and wraps BeginTx failure as "begin is_blocked recompute: <cause>". It throws when the connection cannot start a transaction — the database is closed, the connection is broken, or the driver rejects the isolation level. No recompute work has happened yet, so it is safe to retry.
Solutions
- Check that the DB connection is open and healthy before calling
- Retry with backoff if the cause is a transient connection/lock error
- Inspect the wrapped cause for driver-specific lock or close messages
- Avoid calling concurrently with operations that hold long write transactions
Example fix
// before
n, err := vcops.RecomputeAllBlockedOnConn(ctx, conn, "agent")
// after
if err := conn.PingContext(ctx); err != nil {
return fmt.Errorf("db unavailable: %w", err)
}
n, err := vcops.RecomputeAllBlockedOnConn(ctx, conn, "agent")
if err != nil { return fmt.Errorf("recompute: %w", err) } Defensive patterns
Strategy: retry
Validate before calling
if err := conn.PingContext(ctx); err != nil {
return fmt.Errorf("db not ready: %w", err)
} Try / catch
n, err := vcops.RecomputeAllBlockedOnConn(ctx, conn, author)
if err != nil {
if strings.Contains(err.Error(), "begin is_blocked recompute") && isTransient(errors.Unwrap(err)) {
return retryWithBackoff(3, func() error {
_, e := vcops.RecomputeAllBlockedOnConn(ctx, conn, author); return e
})
}
return err
} Prevention
- Ping the connection before long recompute jobs
- Avoid running during shutdown or while db.Close() may execute
- Keep transactions short to reduce lock contention with other writers
When it happens
Trigger: RecomputeAllBlockedOnConn(ctx, conn, author) when conn.BeginTx fails: connection closed/pooled-broken, database locked by another writer in restrictive drivers, or invalid tx options.
Common situations: Calling after db.Close() or during shutdown; connection dropped by a network hiccup or server idle timeout; concurrent long-running writer transactions blocking new ones.
Related errors
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/05527517fb198f9c.
Report an issue: GitHub.
Appendix: source
Thrown at internal/storage/versioncontrolops/blocked_recompute.go:73
// RecomputeAllBlockedOnConn runs the whole full is_blocked repair on conn and
// returns the number of rows it corrected: guard + recompute inside one
// transaction, then — only when something actually changed — stage `issues`
// alone and commit under BlockedRecomputeCommitMsg.
//
// author may be empty, which omits --author and lets the server attribute the
// commit to the connected session. That is the right default for the
// proxied-server plane, whose every other commit (uow.Tx.Commit) is likewise
// unauthored; a store that has a configured committer identity passes it.
//
// The returned count is meaningful even alongside a non-nil error from the
// staging step: the rows WERE corrected in the working set, only the history
// entry failed, and a caller that reported 0 there would be lying about the
// database it is looking at.
func RecomputeAllBlockedOnConn(ctx context.Context, conn BlockedRecomputeConn, author string) (int64, error) {
tx, err := conn.BeginTx(ctx, nil)
if err != nil {
return 0, fmt.Errorf("begin is_blocked recompute: %w", err)
}
changed, err := GuardedRecomputeAllBlockedInTx(ctx, tx)
if err != nil {
_ = tx.Rollback()
return 0, err
}
if err := tx.Commit(); err != nil {
return 0, fmt.Errorf("commit is_blocked recompute: %w", err)
}
if changed > 0 {
if err := StageAndCommit(ctx, conn, BlockedRecomputeStagedTables(), BlockedRecomputeCommitMsg, author); err != nil {
return changed, err
}
}
return changed, nil
}
View on GitHub (pinned to 71377f2769)