gastownhall/beads · critical
schema migration: %w
Error message
schema migration: %w
What it means
This wraps any failure from schema.MigrateUpWithLock, which runs pending schema migrations on the pinned connection while holding a migration lock. The database may be left partially migrated ('dirty') if a migration itself failed midway, so this error should never be blindly retried without checking migration state.
Source
Thrown at internal/storage/dolt/store.go:2610
) (int, error) {
conn, err := db.Conn(ctx)
if err != nil {
return 0, fmt.Errorf("schema: pin connection: %w", err)
}
defer conn.Close()
var dbName string
if err := conn.QueryRowContext(ctx, "SELECT DATABASE()").Scan(&dbName); err != nil {
return 0, fmt.Errorf("schema: read database name: %w", err)
}
var opts []schema.MigrateLockOption
if bootstrapHeal != nil {
opts = append(opts, schema.WithFreshBootstrapHeal(bootstrapHeal, endpoint))
}
applied, err := schema.MigrateUpWithLock(ctx, conn, dbName, opts...)
if err != nil {
return applied, fmt.Errorf("schema migration: %w", err)
}
return applied, nil
}
func initSchemaOnDBWithRetry(ctx context.Context, db *sql.DB) (int, error) {
return initSchemaOnDBWithRetryAndGate(ctx, db, nil)
}
// initSchemaOnDBWithRetryAndGate is initSchemaOnDBWithRetry with an optional
// pre-migration gate run INSIDE the retry loop. The gate's own reads
// (schema_migrations, dolt_remotes) can hit the same transient Dolt
// startup/catalog races the migration retry absorbs, so gate probe errors are
// retried with them instead of failing the open fast (bd-6dnrw.30); a
// *schema.RemoteMigrateGateError refusal stays permanent.
func initSchemaOnDBWithRetryAndGate(ctx context.Context, db *sql.DB, gate func(context.Context, *sql.DB) error) (int, error) {
return initSchemaOnDBWithRetryAndGateBootstrapHeal(ctx, db, gate, nil, "")
}
View on GitHub (pinned to 71377f2769)
Solutions
- Read the wrapped error to see which migration step failed; inspect the schema_migrations/table state before retrying
- Ensure only one process runs migrations at a time (single-instance init or external lock/leader election)
- Fix or roll back the failed migration, then re-run
- Verify the Dolt SQL server supports every statement in the failing migration
Defensive patterns
Strategy: retry
Validate before calling
// check migration state before re-running
var dirty bool
var version int
row := conn.QueryRowContext(ctx,
"SELECT version, dirty FROM schema_migrations LIMIT 1")
if err := row.Scan(&version, &dirty); err == nil && dirty {
// resolve dirty state manually before retrying migrations
} Try / catch
// Go: never auto-retry blindly; detect dirty state
applied, err := schema.MigrateUpWithLock(ctx, conn, dbName, opts...)
if err != nil {
var lockErr *schema.LockError
if errors.As(err, &lockErr) {
// another migrator holds the lock: back off and retry
} else {
// migration itself failed: inspect schema_migrations, don't blind-retry
}
return err
} Prevention
- Run migrations from a single process (leader election or init container)
- Treat any migration failure as requiring manual state inspection
- Keep migration steps idempotent and small
- Test migrations against Dolt before production rollout
When it happens
Trigger: Calling schema init/migrate when: a migration SQL statement fails, the migration lock cannot be acquired (another process migrating), the schema_migrations table is inconsistent, or the connection drops mid-migration.
Common situations: Concurrent processes (two app instances) racing to migrate at startup, a previously failed migration left dirty state, Dolt server killed during migration, schema migration files referencing unsupported Dolt SQL features.
Related errors
- schema: read database name: %w
- schema: pin connection: %w
- failed to open migration connection: %w
- failed to insert initial issue counter for prefix %q: %w
- failed to check existing counter for prefix %q: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/6d2e306626191b1e.
Report an issue: GitHub.