golang-migrate "Dirty database version" in CI
golang-migrate marks a version "dirty" when a migration starts but does not finish cleanly. It then refuses to run further migrations until you fix the schema and explicitly force it to a known clean version. This is a recovery step, not a retry.
What this error means
migrate up fails immediately reporting a dirty database version and telling you to fix and force. It is deterministic - every run stops at the same dirty marker until you clear it.
error: Dirty database version 5. Fix and force version.Common causes
A migration failed mid-apply
A migration errored (bad SQL, timeout, interrupted run) after the version was marked started, leaving the schema_migrations row flagged dirty.
Non-transactional partial change
On engines/statements without transactional DDL, part of the migration applied before it failed, so the schema is in an in-between state.
How to fix it
Reconcile the schema, then force a clean version
Determine whether the dirty migration’s changes are present, undo any partial state, then force the version that matches reality.
# inspect what actually applied, undo partial changes if needed, then:
migrate -path ./migrations -database "$DB_URL" force 4
migrate -path ./migrations -database "$DB_URL" upFix the migration that failed
- Read the original failure and correct the migration SQL.
- Wrap statements in a transaction where the engine supports it so failures leave no partial state.
- Run against a clean database in CI to confirm the fix.
How to prevent it
- Use transactional migrations where supported so a failure leaves nothing dirty.
- Run migrations against ephemeral databases in CI.
- Resolve a dirty version immediately instead of stacking new migrations.