{"record":{"id":"b095a4fda816518d","repo":"golang-migrate/migrate","slug":"database-is-dirty-b095a4","errorCode":null,"errorMessage":"database is dirty","messagePattern":"database is dirty","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"database/sqlcipher/sqlcipher.go","lineNumber":24,"sourceCode":"\t\"fmt\"\n\t\"io\"\n\tnurl \"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync/atomic\"\n\n\t\"github.com/golang-migrate/migrate/v4\"\n\t\"github.com/golang-migrate/migrate/v4/database\"\n\t_ \"github.com/mutecomm/go-sqlcipher/v4\"\n)\n\nfunc init() {\n\tdatabase.Register(\"sqlcipher\", &Sqlite{})\n}\n\nvar DefaultMigrationsTable = \"schema_migrations\"\nvar (\n\tErrDatabaseDirty  = fmt.Errorf(\"database is dirty\")\n\tErrNilConfig      = fmt.Errorf(\"no config\")\n\tErrNoDatabaseName = fmt.Errorf(\"no database name\")\n)\n\ntype Config struct {\n\tMigrationsTable string\n\tDatabaseName    string\n\tNoTxWrap        bool\n}\n\ntype Sqlite struct {\n\tdb       *sql.DB\n\tisLocked atomic.Bool\n\n\tconfig *Config\n}\n\nfunc WithInstance(instance *sql.DB, config *Config) (database.Driver, error) {","sourceCodeStart":6,"sourceCodeEnd":42,"githubUrl":"https://github.com/golang-migrate/migrate/blob/01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a/database/sqlcipher/sqlcipher.go#L6-L42","documentation":"ErrDatabaseDirty is a sentinel error returned by the sqlcipher driver when the schema_migrations table indicates a previous migration run did not complete (a record is missing a success/commit marker). The driver refuses to run further migrations to avoid operating on an inconsistently migrated schema. It is returned from WithInstance/Open (during RunMigrations) until the dirty state is resolved.","triggerScenarios":"Calling WithInstance or Open (then migrate.Up/Down) on a sqlcipher database whose migrations table contains a row with dirty=true — i.e., a previous migration was interrupted mid-run (process killed, crash, lost connection) leaving the schema in an unknown state.","commonSituations":"Migration process killed by Ctrl+C, SIGKILL, OOM, or deploy timeout halfway through an ALTER TABLE; power loss during migration; a partial migration in a container that was restarted; failed transaction rollback leaving a dirty flag.","solutions":["Inspect the schema to determine whether the last (partial) migration actually applied.","If it did apply, manually set the migrations table row to clean: UPDATE schema_migrations SET dirty=false WHERE version=<version>;","If it did not apply, manually drop the partially applied changes and the dirty row, then re-run migrate.Up.","Restore the database from a backup taken before the failed migration, then re-run migrations.","Never edit the migrations table while unsure of schema state — verify against the migration SQL first."],"exampleFix":"// inspect then fix\n// before\n// panic: Dirty database version 5. Fix and force version.\n// after (sqlite/sqlcipher shell)\n// sqlite3 app.db \"SELECT * FROM schema_migrations;\"\n// -- 5|1  (dirty)\n// sqlite3 app.db \"UPDATE schema_migrations SET dirty=0 WHERE version=5;\"","handlingStrategy":"type-guard","validationCode":"// before migrating\nrows, err := db.Query(\"SELECT version, dirty FROM schema_migrations\")\nif err == nil {\n    for rows.Next() {\n        var v int; var d bool\n        rows.Scan(&v, &d)\n        if d { return fmt.Errorf(\"db dirty at version %d; resolve before migrating\", v) }\n    }\n}","typeGuard":"func isDirtyErr(err error) bool {\n    return errors.Is(err, sqlcipher.ErrDatabaseDirty)\n}","tryCatchPattern":"if err := m.Up(); err != nil {\n    if errors.Is(err, sqlcipher.ErrDatabaseDirty) {\n        // halt pipeline; inspect schema and fix schema_migrations manually\n        return fmt.Errorf(\"migration halted: dirty database: %w\", err)\n    }\n    return err\n}","preventionTips":["Run migrations in a supervised context; never SIGKILL a running migrator.","Take a DB backup before running migrations so you can restore instead of hand-editing state.","Run migrations as a dedicated deploy step with generous timeouts.","Alert on dirty=true rows in schema_migrations."],"tags":["sqlcipher","sqlite","migration","dirty-database","go"],"backgroundTag":"database-migration-dirty-state","analyzedSha":"01a9643f1475e75bb6d6224ddeaf9d8e2434ca8a","analyzedAt":"2026-09-02T19:38:29.671Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-10T02:17:09.455Z"}