golang-migrate/migrate · critical

database is dirty

Error message

database is dirty

What it means

ErrDatabaseDirty signals the migrations table records a migration version with a dirty flag set, meaning a previous migration failed mid-execution (partially applied) and the schema state is unknown. The driver refuses further migrations until a human resolves the state.

Source

Thrown at database/sqlserver/sqlserver.go:31

	"github.com/Azure/go-autorest/autorest/adal"
	"github.com/golang-migrate/migrate/v4"
	"github.com/golang-migrate/migrate/v4/database"
	mssql "github.com/microsoft/go-mssqldb" // mssql support
)

func init() {
	database.Register("sqlserver", &SQLServer{})
}

// DefaultMigrationsTable is the name of the migrations table in the database
var DefaultMigrationsTable = "schema_migrations"

var (
	ErrNilConfig                 = fmt.Errorf("no config")
	ErrNoDatabaseName            = fmt.Errorf("no database name")
	ErrNoSchema                  = fmt.Errorf("no schema")
	ErrDatabaseDirty             = fmt.Errorf("database is dirty")
	ErrMultipleAuthOptionsPassed = fmt.Errorf("both password and useMsi=true were passed")
)

var lockErrorMap = map[int]string{
	-1:   "The lock request timed out.",
	-2:   "The lock request was canceled.",
	-3:   "The lock request was chosen as a deadlock victim.",
	-999: "Parameter validation or other call error.",
}

// Config for database
type Config struct {
	MigrationsTable string
	DatabaseName    string
	SchemaName      string
}

// SQL Server connection

View on GitHub (pinned to 01a9643f14)

Solutions

  1. Inspect the schema_migrations table: check the version and dirty flag, and verify whether the failing migration's DDL actually applied
  2. Manually fix the schema (complete or roll back the partial migration), then set dirty=false (or force the version)
  3. Use migrate.Force(version) via CLI or driver to clear the dirty state once the database matches the recorded version, then re-run migrations
  4. Prevent recurrence by running migrations in a transaction-backed driver or with a lock/single runner

Example fix

-- before (dirty state)
SELECT * FROM schema_migrations; -- version 7, dirty 1
-- after verifying migration 7 fully applied
UPDATE schema_migrations SET dirty = 0 WHERE version = 7;
Defensive patterns

Strategy: validation

Validate before calling

var version uint; var dirty bool
row := db.QueryRow("SELECT version, dirty FROM schema_migrations")
if err := row.Scan(&version, &dirty); err == nil && dirty {
    return fmt.Errorf("database dirty at version %d; resolve manually before migrating", version)
}

Try / catch

if errors.Is(err, sqlserver.ErrDatabaseDirty) {
    // inspect schema_migrations, fix schema state, then:
    // migrate.Force(knownGoodVersion) before retrying Up
}

Prevention

When it happens

Trigger: A prior migration panicked, the process crashed, or the connection dropped while a migration was applying, leaving dirty=true on the version row; retrying migrate.Up/Stef afterwards.

Common situations: Long DDL migrations interrupted by deploys or OOM kills; network partitions during migration; concurrent migration processes after a failed run.

Related errors


AI-assisted analysis of golang-migrate/migrate@01a9643f14 (2026-09-02). Data as JSON: /api/errors/8bf2360ed3311c78. Report an issue: GitHub.