gastownhall/beads · error
checking pre-existing dirty table diffs: %w
Error message
checking pre-existing dirty table diffs: %w
What it means
After all migrations and backfills apply, MigrateUp compares signatures of pre-existing dirty tables against snapshots taken before the pass (changedDirtyTableSignatures) to prove the migration never mutated user data in dirty tables. This error wraps a failure of that comparison query itself — it is a diagnostic failure, not evidence of data change. MigrateUp aborts before staging/committing so a broken verification never lets an unverifiable pass be checkpointed.
Source
Thrown at internal/storage/schema/schema.go:724
// there is no dirty-commit recovery story to support here the way
// there is for the main-source guard (#4566 scope).
return applied, fmt.Errorf("pending ignored schema migrations alter pre-existing dirty tables: %s", strings.Join(touchedIgnoredDirtyTables, ", "))
}
appliedIgnored, ignoredColumnAdded, err := ignoredSource.migrate(ctx, db, 0)
if err != nil {
return applied, fmt.Errorf("ignored migrations: %w", err)
}
if err := unstageIgnoredTables(ctx, db); err != nil {
return applied, fmt.Errorf("unstaging ignored migration tables: %w", err)
}
if applied == 0 && !backfilled && appliedIgnored == 0 && !mainColumnAdded && !ignoredColumnAdded {
return applied, nil
}
changedDirtyTables, err := changedDirtyTableSignatures(ctx, db, dirtyBeforeSignatures)
if err != nil {
return applied, fmt.Errorf("checking pre-existing dirty table diffs: %w", err)
}
if len(changedDirtyTables) > 0 {
return applied, fmt.Errorf("pre-existing dirty tables changed during schema migration: %s", strings.Join(changedDirtyTables, ", "))
}
staged, err := stageSchemaTables(ctx, db, dirtyBefore)
if err != nil {
return applied, fmt.Errorf("staging migrations: %w", err)
}
if !staged {
return applied, nil
}
if err := DrainCall(ctx, db, "CALL DOLT_COMMIT('-m', 'schema: apply migrations')"); err != nil {
if !strings.Contains(strings.ToLower(err.Error()), "nothing to commit") {
return applied, fmt.Errorf("committing migrations: %w", err)
}
}
View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped cause (%w) for the failing introspection query and fix the underlying SQL/driver error
- Ensure no concurrent process is altering or dropping tables during migration — run MigrateUp under the advisory lock (MigrateUpWithLock) and stop competing writers, then re-run
- Verify the database connection is stable (no mid-pass disconnects) and re-run on a healthy connection
- If a dirty table's metadata is unreadable/corrupt, repair or restore that table before migrating again
- Re-run MigrateUp after fixing; migrations already applied, only verification and staging were skipped
Example fix
// before: migrating without a lock while another writer drops tables _, err := schema.MigrateUp(ctx, db) // after: take the advisory lock so no concurrent DDL races the diff check _, err := schema.MigrateUpWithLock(ctx, db)
Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: stable connection and no concurrent DDL expected
if err := db.PingContext(ctx); err != nil {
return fmt.Errorf("unstable connection before migrate: %w", err)
}
// Check for dirty tables whose state the diff check will read
// (MigrateUp itself guards against dirty tables; verify lock availability)
// Prefer the locked entry point:
// applied, err := schema.MigrateUpWithLock(ctx, db) Try / catch
applied, err := schema.MigrateUpWithLock(ctx, db)
if err != nil {
if strings.Contains(err.Error(), "checking pre-existing dirty table diffs:") {
cause := errors.Unwrap(err)
return fmt.Errorf("migration diff verification failed (%v); migrations applied but not staged/committed — fix and re-run", cause)
}
return err
} Prevention
- Use MigrateUpWithLock to exclude concurrent DDL from racing the diff check
- Avoid dropping/renaming tables manually while an upgrade is in progress
- Keep connections alive for the duration of long migrations (raise timeouts)
- Repair corrupt tables before migrating so signature queries can read them
When it happens
Trigger: Calling MigrateUp/MigrateUpWithLock when changedDirtyTableSignatures(ctx, db, dirtyBeforeSignatures) errors — e.g. the dirty-table signature query fails because a dirty table was dropped/renamed mid-pass, a SQL/driver error occurs while reading table signatures, or the connection fails during the check.
Common situations: Another process mutating schema (dropping tables) while MigrateUp runs, corrupted table metadata that the signature query cannot read, connection timeouts on large databases between the pre- and post-pass reads, or driver-level SQL errors in the signature introspection queries.
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/92f45a6b5d604992.
Report an issue: GitHub.