gastownhall/beads · error
reading pre-migration schema version: %w
Error message
reading pre-migration schema version: %w
What it means
This error wraps a failure reading the current main-source schema version (mainSource.currentVersion) before any migration runs. The captured version lets the aux re-key distinguish a first rekey-aware pass from a fresh clone of an already-converged lineage (bd-578h9.4). Without it, MigrateUp cannot run safely and aborts with nothing applied.
Source
Thrown at internal/storage/schema/schema.go:652
}
touchedDirtyTables, err := mainSource.pendingMigrationDirtyTables(ctx, db, dirtyBefore)
if err != nil {
return 0, fmt.Errorf("checking dirty tables against pending migrations: %w", err)
}
if len(touchedDirtyTables) > 0 {
return 0, &DirtyTablesError{Tables: touchedDirtyTables}
}
dirtyBeforeSignatures, err := dirtyTableSignatures(ctx, db, dirtyBefore)
if err != nil {
return 0, fmt.Errorf("reading pre-migration dirty table diffs: %w", err)
}
// Captured before the main migrations run: the aux re-key uses it to
// distinguish the lineage's first rekey-aware migration (run the pass)
// from a fresh clone of an already-converged lineage (record the marker
// only, bd-578h9.4).
mainVersionBefore, err := mainSource.currentVersion(ctx, db)
if err != nil {
return 0, fmt.Errorf("reading pre-migration schema version: %w", err)
}
applied, mainColumnAdded, err := mainSource.migrate(ctx, db, 0)
if err != nil {
return applied, err
}
backfilled, err := ensureBackfilledCustomStatusesCustomTypes(ctx, db)
if err != nil {
return applied, fmt.Errorf("backfill custom tables: %w", err)
}
// #4259: rewrite any per-clone-random dependency ids (minted by 0043's
// DEFAULT (UUID()) before this fix) to the deterministic key, so independently
// migrated clones converge to byte-identical, merge-safe dependencies. Runs
// here, after the schema migrations (0050 has asserted the canonical schema),
// and only on a pass where migration work was needed.
rekeyed, err := rekeyDependencyIDs(ctx, db)View on GitHub (pinned to 71377f2769)
Solutions
- Fix the wrapped cause (query failure on the version cursor table)
- Ensure the schema_migrations table exists and contains a valid version row; recreate it from a healthy clone if missing
- Never hand-edit migration cursors - let migrations own them
- Retry MigrateUp once cursor reads succeed
Defensive patterns
Strategy: validation
Validate before calling
// Confirm the migration cursor exists before opening
row := db.QueryRowContext(ctx,
"SELECT COUNT(*) FROM schema_migrations")
var n int
if err := row.Scan(&n); err != nil || n == 0 {
return errors.New("schema_migrations missing or empty; restore from a healthy clone")
} Type guard
func isVersionCursorErr(err error) bool {
return err != nil && strings.Contains(err.Error(), "reading pre-migration schema version")
} Try / catch
if _, err := schema.MigrateUp(ctx, db); err != nil {
if isVersionCursorErr(err) {
return fmt.Errorf("migration cursor broken, refusing to guess version: %w", err)
}
return err
} Prevention
- Never hand-edit or delete schema_migrations rows
- Copy whole databases, not individual tables, so cursors travel with data
- Let MigrateUp exclusively manage version state
When it happens
Trigger: Calling MigrateUp/MigrateUpWithLock when the schema version cursor cannot be read - missing or unreadable schema_migrations table, corrupt version row, or a Dolt query error on the cursor table.
Common situations: Databases copied out-of-band without the schema_migrations table; a manual edit/deletion of the migrations cursor; storage-level failures on a read-only or damaged repo.
Related errors
- failed to migrate credential keys: %w
- failed to update encrypted password for peer %s: %w
- failed to initialize schema: %w
- failed to rebuild pool after migration: %w
- ensuring local_metadata: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/926f6543fd557b8b.
Report an issue: GitHub.