gastownhall/beads · error

failed to begin ignored tx: %w

Error message

failed to begin ignored tx: %w

What it means

After verifying the borrowed session is on the right branch, beginBorrowedTx calls conn.BeginTx to start the ignored-tables transaction. This wraps a failure to BEGIN on that borrowed connection — the session died between the branch read and BEGIN, ctx was canceled, or the server rejected the transaction. The caller discards the connection and falls back to a fresh dial, so repeated occurrences point at server instability.

Source

Thrown at internal/storage/dolt/transaction.go:397

// shipped flow diverges a pool session's branch from the regular tx's branch
// (DoltStore.Checkout has no non-test callers), so this is defense in depth
// for future Checkout callers and for multi-connection tests.
//
// Instead of an unconditional checkout it verifies the session is already on
// the requested branch — the overwhelmingly common case — and sends the
// caller to the fresh-dial fallback otherwise. Same round-trip count as the
// checkout it replaces (one statement), so the borrow fast path stays free.
func beginBorrowedTx(ctx context.Context, conn *sql.Conn, branch string) (*sql.Tx, error) {
	var active string
	if err := conn.QueryRowContext(ctx, "SELECT active_branch()").Scan(&active); err != nil {
		return nil, fmt.Errorf("failed to read borrowed conn's active branch: %w", err)
	}
	if active != branch {
		return nil, fmt.Errorf("borrowed conn is on branch %q, want %q: refusing to switch a pooled session's branch", active, branch)
	}
	tx, err := conn.BeginTx(ctx, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to begin ignored tx: %w", err)
	}
	return tx, nil
}

// beginTxOnConn checks a connection out to branch and begins a transaction on
// it. Only the fallback path uses it: the fallback owns a dedicated
// single-connection pool, so checking its session out is safe. Every Dolt SQL
// session has its own active branch, so the explicit checkout is required on
// a fresh dial.
func beginTxOnConn(ctx context.Context, conn *sql.Conn, branch string) (*sql.Tx, error) {
	if _, err := conn.ExecContext(ctx, "CALL DOLT_CHECKOUT(?)", branch); err != nil {
		return nil, fmt.Errorf("failed to checkout ignored tx branch %s: %w", branch, err)
	}
	tx, err := conn.BeginTx(ctx, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to begin ignored tx: %w", err)
	}
	return tx, nil

View on GitHub (pinned to 71377f2769)

Solutions

  1. Retry — the borrow path falls back to a fresh dial automatically; if it surfaces repeatedly, investigate server stability
  2. Check Dolt server logs at failure time for shutdown or storage errors
  3. Widen ctx deadlines if cancellation is the cause
  4. Keep ConnMaxLifetime below server idle/session timeouts so pooled sessions stay healthy
Defensive patterns

Strategy: retry

Validate before calling

if err := db.PingContext(ctx); err != nil { return fmt.Errorf("pool sessions unhealthy: %w", err) }

Try / catch

if err != nil && errors.Is(err, context.Canceled) {
    return err // caller canceled: do not retry
}
if err != nil {
    return freshDialFallback(ctx) // session died: safe to retry on a new connection
}

Prevention

When it happens

Trigger: Connection dropped between `SELECT active_branch()` and BEGIN; ctx deadline/cancellation hitting BeginTx; Dolt server refusing new transactions (shutdown, storage error); session killed server-side.

Common situations: Hosted-gateway load balancers killing sessions mid-use; server restarts; tight context deadlines; embedded Dolt under memory pressure.

Related errors


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