gastownhall/beads · error

ErrTransaction

ErrTransaction

Error message

transaction error

What it means

ErrTransaction is the dolt storage layer's sentinel for begin/commit/rollback failures. wrapTransactionError embeds it into the returned error chain (fmt.Errorf with %w), so callers can classify any wrapped failure with errors.Is without string matching.

Source

Thrown at internal/storage/dolt/errors.go:22

	"database/sql"
	"errors"
	"fmt"
	"os"
	"path/filepath"
	"strings"

	mysql "github.com/go-sql-driver/mysql"

	"github.com/steveyegge/beads/internal/storage"
	"github.com/steveyegge/beads/internal/storage/dberrors"
)

// Sentinel errors for the dolt storage layer.
// These complement the storage-level sentinels (storage.ErrNotFound, etc.)
// with dolt-specific error types.
var (
	// ErrTransaction indicates a transaction begin/commit/rollback failure.
	ErrTransaction = errors.New("transaction error")

	// ErrQuery indicates a database query failure.
	ErrQuery = errors.New("query error")

	// ErrScan indicates a failure scanning database rows into Go values.
	ErrScan = errors.New("scan error")

	// ErrExec indicates a database exec (INSERT/UPDATE/DELETE) failure.
	ErrExec = errors.New("exec error")

	// ErrDanglingReference indicates that the pre-push integrity check detected
	// missing chunks in the local Dolt noms store. The push was aborted to
	// prevent propagating the corruption to the remote. Run bd dolt verify
	// to diagnose and recover.
	ErrDanglingReference = errors.New("dangling chunk reference")

	// ErrFSCKTimeout indicates that the pre-push integrity check (dolt fsck) did
	// not complete within the configured timeout. The push was aborted without

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check errors.Is(err, dolt.ErrTransaction) to classify, then unwrap (errors.Unwrap / %w chain) for the underlying MySQL error
  2. Retry once on serialization-class causes (deadlock 1213, lock wait timeout 1205) — the transaction was rolled back
  3. Check server health and connectivity (bd doctor) if the cause is a dropped connection
  4. Reduce transaction scope/duration to avoid lock timeouts under concurrency

Example fix

// before
if err := store.Update(ctx, issue); err != nil { return err } // opaque failure
// after
if err := store.Update(ctx, issue); err != nil {
    if errors.Is(err, dolt.ErrTransaction) {
        var mysqlErr *mysql.MySQLError
        if errors.As(err, &mysqlErr) && (mysqlErr.Number == 1213 || mysqlErr.Number == 1205) {
            return store.Update(ctx, issue) // safe retry: rolled back
        }
    }
    return err
}
Defensive patterns

Strategy: type-guard

Type guard

func isTransactionError(err error) bool {
    return errors.Is(err, dolt.ErrTransaction)
}
func isRetryableTxn(err error) bool {
    var mysqlErr *mysql.MySQLError
    return errors.As(err, &mysqlErr) && (mysqlErr.Number == 1213 || mysqlErr.Number == 1205)
}

Try / catch

if err := op(ctx); err != nil {
    if errors.Is(err, dolt.ErrTransaction) {
        if isRetryableTxn(err) {
            return retry(op, ctx) // rolled back; safe to retry with backoff
        }
        return fmt.Errorf("transaction failed permanently: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Any dolt-backed storage operation whose Begin/Commit/Rollback fails — connection drop or timeout during commit, MySQL lock wait timeout (1205) or deadlock (1213) at commit, Dolt merge-conflict autocommit rollback, or a cancelled context killing the transaction mid-flight. The op-specific message and the underlying error are wrapped alongside it.

Common situations: Two processes writing concurrently and deadlocking; server restarted mid-transaction; network blip between client and Dolt server; long transaction exceeding lock timeout; merge conflict on commit in multi-branch setups.

Related errors


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