gastownhall/beads · error

failed to begin transaction: %w

Error message

failed to begin transaction: %w

What it means

repairBlockedState starts an explicit SQL transaction before recomputing is_blocked, so writes persist even on a Dolt server running with --no-auto-commit. This error wraps the failure of db.Begin() itself — the transaction could not be opened at all, so no repair work was attempted. It most commonly indicates the connection to the Dolt SQL server is broken, closed, or refused.

Source

Thrown at cmd/bd/doctor/fix/blocked.go:53

		return nil
	}
	defer db.Close()

	if skip, err := guardFixTarget("Blocked-state fix", db, beadsDir, cfg); skip {
		return err
	}

	return repairBlockedState(context.Background(), db)
}

// repairBlockedState recomputes is_blocked on an open connection. Split from
// RecomputeBlocked so the repair is testable against an existing store handle.
func repairBlockedState(ctx context.Context, db *sql.DB) error {
	// Explicit transaction so writes persist when @@autocommit is OFF (e.g. a
	// Dolt server started with --no-auto-commit).
	tx, err := db.Begin()
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	// Refuse to derive and commit is_blocked from a dirty graph: like the store
	// paths, the recompute reads the working set and stages only `issues`, so a
	// dirty issues/dependencies tree would taint the repair commit (bd-6dnrw.37).
	// In a `bd doctor --fix` run the dependency-graph fixes commit ahead of this
	// one, so the tree is normally clean here; when it is not, surface it as an
	// actionable error rather than committing tainted state.
	if err := issueops.GuardBlockedRecomputeWorkingSet(ctx, tx); err != nil {
		_ = tx.Rollback()
		return err
	}
	changed, err := issueops.RecomputeAllIsBlockedInTx(ctx, tx)
	if err != nil {
		_ = tx.Rollback()
		return fmt.Errorf("failed to recompute is_blocked: %w", err)
	}
	if err := tx.Commit(); err != nil {
		return fmt.Errorf("failed to commit is_blocked repairs: %w", err)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Rerun `bd doctor --fix` — this is often a transient dead-connection failure and a fresh openDoltDB connection will succeed.
  2. Verify the Dolt SQL server is running and reachable (check the server config in .beads and the port); restart it if needed.
  3. Check server logs for max_connections exhaustion or auth/session errors and raise limits or fix credentials as needed.
  4. If the Dolt server runs with restrictive settings, ensure it supports transactional BEGIN (standard `dolt sql-server` does; a proxy or read-only replica may not).

Example fix

// before (server went away mid-run)
err: failed to begin transaction: driver: bad connection

// after: retry with a fresh handle
if err := repairBlockedState(ctx, db); err != nil {
	db.Close()
	db, cfg, err = openDoltDB(beadsDir)
	if err == nil {
		err = repairBlockedState(ctx, db)
	}
}
Defensive patterns

Strategy: retry

Validate before calling

// Probe the connection before the repair
if err := db.PingContext(ctx); err != nil {
	return fmt.Errorf("dolt server unreachable, restart it before doctor --fix: %w", err)
}

Type guard

func isOpenDB(db *sql.DB) bool {
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	return db.PingContext(ctx) == nil
}

Try / catch

if err := repairBlockedState(ctx, db); err != nil && strings.Contains(err.Error(), "failed to begin transaction") {
	// connection was dead — reopen and retry once
	db.Close()
	db, _, err = openDoltDB(beadsDir)
	if err == nil {
		err = repairBlockedState(ctx, db)
	}
}

Prevention

When it happens

Trigger: Calling fix.RecomputeBlocked(path) (which calls repairBlockedState) when db.Begin() on the open *sql.DB fails: the Dolt server connection is dead or timed out, the server rejected the session (e.g. after a restart), the connection pool returned an unusable connection, or the driver failed to issue BEGIN against the server.

Common situations: Dolt server was restarted or crashed between openDoltDB and the repair; network/keepalive timeout dropped an idle pooled connection; server max-connections exhausted; running against a misconfigured port so the 'connection' is not actually a Dolt SQL server.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/d6aba72460e765dd. Report an issue: GitHub.