gastownhall/beads · error

failed to begin transaction: %w

Error message

failed to begin transaction: %w

What it means

repairDependencyKeys wraps all writes in an explicit transaction so repairs persist even when the Dolt server runs with @@autocommit OFF. If db.Begin() fails — the pool cannot obtain a usable connection or start a transaction — this error wraps that cause and the repair aborts with no changes applied.

Source

Thrown at cmd/bd/doctor/fix/dep_keys.go:116

// repairDependencyKeys scans and repairs rekey-backfill leftovers on an open
// connection. Split from DependencyKeys so the repair logic is testable
// against an existing store handle.
func repairDependencyKeys(ctx context.Context, db *sql.DB, verbose bool) error {
	anomalies, err := ScanDependencyKeys(ctx, db)
	if err != nil {
		return fmt.Errorf("failed to scan dependency keys: %w", err)
	}
	if len(anomalies) == 0 {
		fmt.Println("  No dependency key anomalies to fix")
		return nil
	}

	// Uses explicit transaction so writes persist when @@autocommit is OFF
	// (e.g. Dolt server started with --no-auto-commit).
	tx, err := db.Begin()
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	var rekeyed, removed, failed int
	repairedTables := make(map[string]bool)
	for _, a := range anomalies {
		showIndividual := verbose || len(a.MisKeyed)+len(a.NullTarget) < 20
		for _, mk := range a.MisKeyed {
			//nolint:gosec // G201: table is a hardcoded constant, never user input.
			if _, err := tx.Exec(fmt.Sprintf(`UPDATE %s SET id = ? WHERE id = ?`, a.Table), mk[1], mk[0]); err != nil {
				fmt.Printf("  Warning: failed to re-key %s row %s (row keeps its old id): %v\n", a.Table, mk[0], err)
				failed++
				continue
			}
			rekeyed++
			repairedTables[a.Table] = true
			if showIndividual {
				fmt.Printf("  Re-keyed %s row %s → %s\n", a.Table, mk[0], mk[1])
			}
		}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Retry the command — transient connection loss is the most common cause
  2. Ensure the Dolt server is running and healthy (bd doctor / server logs)
  3. Set reasonable pool limits/timeouts (SetMaxOpenConns, ConnMaxLifetime) so connections don't go stale

Example fix

// before
if err := repairDependencyKeys(ctx, db, verbose); err != nil { ... } // failed to begin transaction
// after: warm/validate the connection first
if err := db.PingContext(ctx); err != nil {
	return fmt.Errorf("database unreachable: %w", err)
}
if err := repairDependencyKeys(ctx, db, verbose); err != nil { ... }
Defensive patterns

Strategy: retry

Validate before calling

if err := db.PingContext(ctx); err != nil {
	return fmt.Errorf("DB unreachable, refusing to begin repair transaction: %w", err)
}

Try / catch

err := repairDependencyKeys(ctx, db, verbose)
if err != nil && strings.Contains(err.Error(), "failed to begin transaction") {
	time.Sleep(2 * time.Second)
	err = repairDependencyKeys(ctx, db, verbose) // Begin failure = nothing written; retry safe
}

Prevention

When it happens

Trigger: Calling DependencyKeys/repairDependencyKeys when db.Begin() fails: connection pool exhausted, connection dropped between scan and Begin, or the Dolt server rejecting transaction start (e.g. server shutting down, --no-auto-commit server in a bad state).

Common situations: Long doctor run over a flaky connection where the pool connection goes stale before Begin; Dolt server restarted mid-fix; too many concurrent bd processes holding connections.

Related errors


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