gastownhall/beads · error

begin is_blocked recompute: %w

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.

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)

Solutions

  1. Check that the DB connection is open and healthy before calling
  2. Retry with backoff if the cause is a transient connection/lock error
  3. Inspect the wrapped cause for driver-specific lock or close messages
  4. 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

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.