{"record":{"id":"b0bcb63b974a7a81","repo":"golang-migrate/migrate","slug":"database-is-dirty-b0bcb6","errorCode":null,"errorMessage":"database is dirty","messagePattern":"database is dirty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"database/mysql/mysql.go","lineNumber":33,"sourceCode":"\t\"strconv\"\n\t\"strings\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/go-sql-driver/mysql\"\n\t\"github.com/golang-migrate/migrate/v4/database\"\n)\n\nvar _ database.Driver = (*Mysql)(nil) // explicit compile time type check\n\nfunc init() {\n\tdatabase.Register(\"mysql\", &Mysql{})\n}\n\nvar DefaultMigrationsTable = \"schema_migrations\"\n\nvar (\n\tErrDatabaseDirty    = fmt.Errorf(\"database is dirty\")\n\tErrNilConfig        = fmt.Errorf(\"no config\")\n\tErrNoDatabaseName   = fmt.Errorf(\"no database name\")\n\tErrAppendPEM        = fmt.Errorf(\"failed to append PEM\")\n\tErrTLSCertKeyConfig = fmt.Errorf(\"to use TLS client authentication, both x-tls-cert and x-tls-key must not be empty\")\n)\n\ntype Config struct {\n\tMigrationsTable  string\n\tDatabaseName     string\n\tNoLock           bool\n\tStatementTimeout time.Duration\n}\n\ntype Mysql struct {\n\t// mysql RELEASE_LOCK must be called from the same conn, so\n\t// just do everything over a single conn anyway.\n\tconn     *sql.Conn\n\tdb       *sql.DB","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/golang-migrate/migrate/blob/01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a/database/mysql/mysql.go#L15-L51","documentation":"ErrDatabaseDirty in database/mysql is returned when the schema_migrations table contains a row with a non-empty dirty flag, meaning a previous migration failed midway and the database is in an unknown/partially-applied state. Migrate refuses further migrations until someone inspects the state and clears the flag. It is also defined identically in other drivers (cassandra, pgx), so the message can come from any of them.","triggerScenarios":"A migration previously failed after the version was recorded but before cleanup; running migrate.Up/Steps on a database left dirty by a crashed process; forcing a version with force but not fixing the schema.","commonSituations":"Deploy pipeline killed mid-migration, SQL migration with a syntax/permission error partway through, manual intervention during a migration, shared staging database left dirty by a colleague.","solutions":["Inspect the schema and the failing migration to manually repair or roll back partial changes","Run migrate.Force(version) to reset the recorded version to the actual applied state","Clear the dirty flag in schema_migrations (set dirty=0) once state is verified","Fix the broken migration file before re-running migrations"],"exampleFix":"// before: blind retry fails with ErrDatabaseDirty\nm.Steps(1)\n// after: repair state then force\n// manually verify/fix schema for version N\nif err := m.Force(N); err != nil { return err }\nif err := m.Up(); err != nil { return err }","handlingStrategy":"try-catch","validationCode":"var dirty bool\nrow := sqlDB.QueryRow(\"SELECT dirty FROM schema_migrations LIMIT 1\")\nif err := row.Scan(&dirty); err == nil && dirty {\n    return fmt.Errorf(\"database left dirty by a previous migration; inspect and force before migrating\")\n}","typeGuard":null,"tryCatchPattern":"if err := m.Up(); err != nil {\n    if errors.Is(err, mysql.ErrDatabaseDirty) {\n        // repair schema manually, then: m.Force(actualVersion); m.Up()\n    }\n    return err\n}","preventionTips":["Run migrations in a single serialized pipeline (no concurrent instances)","Wrap each migration in transactions where possible and alert on failure","After a failed deploy, always repair state with Force before re-running","Monitor the schema_migrations dirty flag"],"tags":["go","golang-migrate","mysql","schema-migrations","dirty-database"],"backgroundTag":"database-is-dirty","analyzedSha":"01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a","analyzedAt":"2026-09-02T19:38:29.671Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}