gastownhall/beads · error

failed to begin transaction: %w

Error message

failed to begin transaction: %w

What it means

execWithLongTimeout begins a transaction on the dedicated long-timeout connection to run a long query (e.g. fetch/pull). This error wraps tx.BeginTx failure — the driver could not get a connection or start a transaction within the context deadline.

Source

Thrown at internal/storage/dolt/store.go:2251

// falls back to the remote's configured refspecs (ParseRefSpecs ->
// GetRefSpecs), which are remote config rather than session state, and a
// fetch writes only remote-tracking refs, never the working branch. Neither
// depends on this fresh connection's default checkout.
func (s *DoltStore) execWithLongTimeout(ctx context.Context, query string, args ...any) error {
	cfg, err := mysql.ParseDSN(s.connStr)
	if err != nil {
		return fmt.Errorf("failed to parse DSN for long-timeout connection: %w", err)
	}
	cfg.ReadTimeout = 5 * time.Minute
	db, err := sql.Open("mysql", cfg.FormatDSN())
	if err != nil {
		return fmt.Errorf("failed to open long-timeout connection: %w", err)
	}
	defer db.Close()
	db.SetMaxOpenConns(1)
	tx, err := db.BeginTx(ctx, nil)
	if err != nil {
		return fmt.Errorf("failed to begin transaction: %w", err)
	}
	if _, err := tx.ExecContext(ctx, query, args...); err != nil {
		_ = tx.Rollback()
		return err
	}
	return tx.Commit()
}

// execWithLongTimeoutNoTx executes a long-running Dolt stored procedure without
// an explicit transaction. Push operations do not need the pull/merge conflict
// handling above, and DOLT_PUSH has diverged from direct `dolt push` behavior
// when wrapped in a SQL transaction.
//
// Audited for be-b0am's fresh-connection branch hazard: safe. Every caller
// passes s.branch explicitly as a CALL DOLT_PUSH(...) arg, so this fresh
// connection's default checkout never matters.
func (s *DoltStore) execWithLongTimeoutNoTx(ctx context.Context, query string, args ...any) error {
	db, err := s.oneShotConn(5 * time.Minute)

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check server reachability (bd dolt status, ping host:port)
  2. Verify credentials and DSN host/port
  3. Retry with a fresh context if the parent deadline expired
  4. Check server logs for connection limits or auth errors

Example fix

// before
ctx := context.Background()
err := store.execWithLongTimeout(ctx, query) // hangs then fails confusingly
// after
ctx, cancel := context.WithTimeout(context.Background(), 6*time.Minute)
defer cancel()
err := store.execWithLongTimeout(ctx, query)
Defensive patterns

Strategy: retry

Validate before calling

if err := db.PingContext(ctx); err != nil {
    return fmt.Errorf("cannot reach server before long query: %w", err)
}
if ctx.Err() != nil { return ctx.Err() }

Try / catch

err := store.execWithLongTimeout(ctx, q)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) || isConnRefused(err) {
        // check server, retry with fresh long context
    }
    return err
}

Prevention

When it happens

Trigger: Calling execWithLongTimeout when db.BeginTx(ctx, nil) fails — pool cannot establish a connection (server down, port wrong, auth rejected) or ctx already cancelled/timed out.

Common situations: Dolt server stopped or restarted; wrong port/host in config; credentials rejected; long-past context deadline from a parent operation.

Understand the failure class

Background: ECONNREFUSED and "connection refused" / "could not connect to server" errors: what they mean and how to fix them — this error's family across 44 libraries.

Related errors


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