gastownhall/beads · error

begin write tx: %w

Error message

begin write tx: %w

What it means

withWriteTx could not begin a SQL transaction on the store's shared connection pool (s.db). BeginTx fails when the pool is closed, the context is cancelled, all pooled connections are dead, or the Dolt server refuses new work. The store surfaces ErrStoreClosed separately, so this error specifically means an open store failed to start its write transaction.

Source

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

				s.breaker.RecordFailure()
				if s.breaker.State() == circuitOpen {
					doltMetrics.circuitTrips.Add(ctx, 1)
					return backoff.Permanent(fmt.Errorf("%w (circuit breaker tripped)", err))
				}
			}
			return err // pre-commit transient: retryable
		}
		return backoff.Permanent(err)
	}, backoff.WithContext(bo, ctx))
}

func (s *DoltStore) withWriteTx(ctx context.Context, fn func(tx *sql.Tx) error) error {
	if s.closed.Load() {
		return ErrStoreClosed
	}
	tx, err := s.db.BeginTx(ctx, nil)
	if err != nil {
		return fmt.Errorf("begin write tx: %w", err)
	}
	clearJournalScope := s.scopeEventsJournalTransaction(tx)
	defer clearJournalScope()
	if err := fn(tx); err != nil {
		return errors.Join(err, tx.Rollback())
	}
	if err := tx.Commit(); err != nil {
		return wrapSQLCommitError("commit write tx", err)
	}
	return nil
}

// SetEventsJournalEnabled activates the journal for this store instance only.
func (s *DoltStore) SetEventsJournalEnabled(enabled bool) {
	s.eventsJournalEnabled.Store(enabled)
}

func (s *DoltStore) scopeEventsJournalTransaction(tx *sql.Tx) func() {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Confirm the Dolt server is up and reachable; restart it if it died, and check the wrapped driver error for the concrete cause.
  2. Check the context: ensure the caller passes a live context with an adequate deadline and does not cancel it early.
  3. Retry — if used via withRetryTx, connection-class failures are retried with backoff; persistent errors indicate a real outage.
  4. If connections keep dying on idle, reduce idle timeouts mismatch (client 10s ReadTimeout vs server read_timeout_millis) or keep traffic warm.

Example fix

// before
tx, err := store.db.BeginTx(ctx, nil)
// after: fail fast with a clear cause
if ctx.Err() != nil {
    return fmt.Errorf("write tx not started, context done: %w", ctx.Err())
}
tx, err := store.db.BeginTx(ctx, nil)
if err != nil {
    return fmt.Errorf("begin write tx: %w", err)
}
Defensive patterns

Strategy: retry

Validate before calling

if err := ctx.Err(); err != nil {
    return fmt.Errorf("context done before write: %w", err)
}
if err := db.PingContext(ctx); err != nil {
    return fmt.Errorf("dolt server unreachable before write: %w", err)
}

Try / catch

err := store.Update(ctx, op)
if err != nil && strings.Contains(err.Error(), "begin write tx") {
    // transient connection issue: retry with backoff, else surface
    if errors.Is(err, driver.ErrBadConn) || isRetryable(err) {
        time.Sleep(backoff)
        err = store.Update(ctx, op)
    }
}

Prevention

When it happens

Trigger: s.db.BeginTx(ctx, nil) returned an error — the Dolt server is unreachable, the pool's connections have timed out (client ReadTimeout of 10s or server read_timeout_millis), ctx was already cancelled, or the driver failed to dial a new pooled connection.

Common situations: Dolt sql-server stopped while the CLI/agent was mid-operation; long idle period let server-side timeouts reap connections; context deadline passed before the call; too many concurrent connections against a small server.

Related errors


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