golang-migrate/migrate · critical

database is dirty

Error message

database is dirty

What it means

ErrDatabaseDirty indicates the migrations table contains a row whose 'dirty' flag is true, meaning a previous migration failed partway (applied but not recorded, or recorded but not applied). The Cassandra driver returns this from Open/WithInstance and refuses to run further migrations until the state is manually resolved. It protects the schema from inconsistent concurrent or partial application.

Source

Thrown at database/cassandra/cassandra.go:34

)

func init() {
	db := new(Cassandra)
	database.Register("cassandra", db)
}

var (
	multiStmtDelimiter = []byte(";")

	DefaultMultiStatementMaxSize = 10 * 1 << 20 // 10 MB
)

var DefaultMigrationsTable = "schema_migrations"

var (
	ErrNilConfig     = errors.New("no config")
	ErrNoKeyspace    = errors.New("no keyspace provided")
	ErrDatabaseDirty = errors.New("database is dirty")
	ErrClosedSession = errors.New("session is closed")
)

type Config struct {
	MigrationsTable       string
	KeyspaceName          string
	MultiStatementEnabled bool
	MultiStatementMaxSize int
}

type Cassandra struct {
	session  *gocql.Session
	isLocked atomic.Bool

	// Open and WithInstance need to guarantee that config is never nil
	config *Config
}

View on GitHub (pinned to 01a9643f14)

Solutions

  1. Inspect the schema_migrations table in the keyspace and determine whether the last migration actually applied.
  2. Fix the schema manually (apply or roll back the partial migration), then force the version: migrate.Force(version) with the correct non-dirty version.
  3. Re-run migrations; if the failure keeps recurring, run migrations from a single serialized process (avoid concurrent deployers).

Example fix

// before (retrying while dirty just fails again)
// after resolving the state manually:
m, _ := migrate.New("cassandra://host/keyspace", "file://migrations")
err := m.Force(3) // set version 3, dirty=false, after repairing schema
Defensive patterns

Strategy: try-catch

Validate before calling

var version, dirty bool
err := session.Query("SELECT dirty FROM " + migrationsTable).Scan(&dirty)
// if dirty is true, resolve before running migrations

Try / catch

if err := m.Up(); err != nil {
    if errors.Is(err, cassandra.ErrDatabaseDirty) {
        // inspect schema_migrations, repair schema, then:
        if ferr := m.Force(lastGoodVersion); ferr != nil { return ferr }
        return m.Up()
    }
    return err
}

Prevention

When it happens

Trigger: A previous migration run crashed or was killed mid-migration; SetVersion was called with dirty=true and never cleared; two migrate processes raced and one left the version row dirty.

Common situations: Deploy pod killed during migration, network drop between applying DDL and updating the version row, developer interrupting a long migration locally.

Related errors


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