gastownhall/beads · warning

failed to read borrowed conn's active branch: %w

Error message

failed to read borrowed conn's active branch: %w

What it means

beginBorrowedTx first queries `SELECT active_branch()` on the borrowed pooled connection to confirm it is already on the regular transaction's branch. This error wraps that read failing — a stale/dead pooled session, ctx cancellation (including the 250ms ignoredTxBorrowTimeout parent), or a non-Dolt server. On error the borrow path falls back to a fresh dial, so this is usually transient.

Source

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

//
// Pool invariant: DOLT_CHECKOUT is session-level, and the borrow cleanup
// returns the connection to the pool as-is — so switching its branch here
// would leak a foreign branch into the pool for an unrelated later caller.
// Every other production checkout site (federation staging, compact, flatten)
// restores the branch before releasing the connection; the borrow path
// preserves the same invariant by refusing instead of switching. Today no
// 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 {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Retry — the code automatically discards the borrowed conn and retries via a fresh-dial fallback, so this only surfaces if you wrap/observe it directly
  2. Tune server idle timeouts (wait_timeout) above your busiest transaction duration
  3. Check ctx deadlines; widen them if the 250ms borrow timeout or parent deadline is expiring
  4. Enable pool health checks / connection lifetimes (ConnMaxLifetime) shorter than server idle timeout

Example fix

// before
db.SetConnMaxLifetime(0) // sessions can idle past server wait_timeout and die
// after
db.SetConnMaxLifetime(30 * time.Second) // recycle before server drops idle sessions
Defensive patterns

Strategy: fallback

Validate before calling

if err := db.PingContext(ctx); err != nil { /* recycle pool before writes */ }

Try / catch

// the library already falls back to a fresh dial; when observing this error:
if err != nil && strings.Contains(err.Error(), "failed to read borrowed conn's active branch") {
    log.Warn("stale borrowed session; fresh-dial fallback engaged")
}

Prevention

When it happens

Trigger: The borrowed pooled connection died since it was parked (server restart, wait_timeout); ctx canceled during the query; server degraded so even trivial queries fail.

Common situations: Idle pools left open across server restarts; hosted-gateway load balancers dropping idle sessions; very short deadlines propagating into transaction setup.

Related errors


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